MDL-69271 auth_ldap: Don't assume any ordering, just verify matches
[moodle.git] / auth / ldap / tests / plugin_test.php
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/>.
17 /**
18  * LDAP authentication plugin tests.
19  *
20  * NOTE: in order to execute this test you need to set up
21  *       OpenLDAP server with core, cosine, nis and internet schemas
22  *       and add configuration constants to config.php or phpunit.xml configuration file:
23  *
24  * define('TEST_AUTH_LDAP_HOST_URL', 'ldap://127.0.0.1');
25  * define('TEST_AUTH_LDAP_BIND_DN', 'cn=someuser,dc=example,dc=local');
26  * define('TEST_AUTH_LDAP_BIND_PW', 'somepassword');
27  * define('TEST_AUTH_LDAP_DOMAIN', 'dc=example,dc=local');
28  *
29  * @package    auth_ldap
30  * @category   phpunit
31  * @copyright  2013 Petr Skoda {@link http://skodak.org}
32  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
33  */
35 defined('MOODLE_INTERNAL') || die();
37 class auth_ldap_plugin_testcase extends advanced_testcase {
39     /**
40      * Data provider for auth_ldap tests
41      *
42      * Used to ensure that all the paged stuff works properly, irrespectively
43      * of the pagesize configured (that implies all the chunking and paging
44      * built in the plugis is doing its work consistently). Both searching and
45      * not searching within subcontexts.
46      *
47      * @return array[]
48      */
49     public function auth_ldap_provider() {
50         $pagesizes = [1, 3, 5, 1000];
51         $subcontexts = [0, 1];
52         $combinations = [];
53         foreach ($pagesizes as $pagesize) {
54             foreach ($subcontexts as $subcontext) {
55                 $combinations["pagesize {$pagesize}, subcontexts {$subcontext}"] = [$pagesize, $subcontext];
56             }
57         }
58         return $combinations;
59     }
61     /**
62      * General auth_ldap testcase
63      *
64      * @dataProvider auth_ldap_provider
65      * @param int $pagesize Value to be configured in settings controlling page size.
66      * @param int $subcontext Value to be configured in settings controlling searching in subcontexts.
67      */
68     public function test_auth_ldap(int $pagesize, int $subcontext) {
69         global $CFG, $DB;
71         if (!extension_loaded('ldap')) {
72             $this->markTestSkipped('LDAP extension is not loaded.');
73         }
75         $this->resetAfterTest();
77         require_once($CFG->dirroot.'/auth/ldap/auth.php');
78         require_once($CFG->libdir.'/ldaplib.php');
80         if (!defined('TEST_AUTH_LDAP_HOST_URL') or !defined('TEST_AUTH_LDAP_BIND_DN') or !defined('TEST_AUTH_LDAP_BIND_PW') or !defined('TEST_AUTH_LDAP_DOMAIN')) {
81             $this->markTestSkipped('External LDAP test server not configured.');
82         }
84         // Make sure we can connect the server.
85         $debuginfo = '';
86         if (!$connection = ldap_connect_moodle(TEST_AUTH_LDAP_HOST_URL, 3, 'rfc2307', TEST_AUTH_LDAP_BIND_DN, TEST_AUTH_LDAP_BIND_PW, LDAP_DEREF_NEVER, $debuginfo, false)) {
87             $this->markTestSkipped('Can not connect to LDAP test server: '.$debuginfo);
88         }
90         $this->enable_plugin();
92         // Create new empty test container.
93         $topdn = 'dc=moodletest,'.TEST_AUTH_LDAP_DOMAIN;
95         $this->recursive_delete($connection, TEST_AUTH_LDAP_DOMAIN, 'dc=moodletest');
97         $o = array();
98         $o['objectClass'] = array('dcObject', 'organizationalUnit');
99         $o['dc']         = 'moodletest';
100         $o['ou']         = 'MOODLETEST';
101         if (!ldap_add($connection, 'dc=moodletest,'.TEST_AUTH_LDAP_DOMAIN, $o)) {
102             $this->markTestSkipped('Can not create test LDAP container.');
103         }
105         // Create a few users.
106         $o = array();
107         $o['objectClass'] = array('organizationalUnit');
108         $o['ou']          = 'users';
109         ldap_add($connection, 'ou='.$o['ou'].','.$topdn, $o);
111         $createdusers = array();
112         for ($i=1; $i<=5; $i++) {
113             $this->create_ldap_user($connection, $topdn, $i);
114             $createdusers[] = 'username' . $i;
115         }
117         // Set up creators group.
118         $assignedroles = array('username1', 'username2');
119         $o = array();
120         $o['objectClass'] = array('posixGroup');
121         $o['cn']          = 'creators';
122         $o['gidNumber']   = 1;
123         $o['memberUid']   = $assignedroles;
124         ldap_add($connection, 'cn='.$o['cn'].','.$topdn, $o);
126         $creatorrole = $DB->get_record('role', array('shortname'=>'coursecreator'));
127         $this->assertNotEmpty($creatorrole);
130         // Configure the plugin a bit.
131         set_config('host_url', TEST_AUTH_LDAP_HOST_URL, 'auth_ldap');
132         set_config('start_tls', 0, 'auth_ldap');
133         set_config('ldap_version', 3, 'auth_ldap');
134         set_config('ldapencoding', 'utf-8', 'auth_ldap');
135         set_config('pagesize', $pagesize, 'auth_ldap');
136         set_config('bind_dn', TEST_AUTH_LDAP_BIND_DN, 'auth_ldap');
137         set_config('bind_pw', TEST_AUTH_LDAP_BIND_PW, 'auth_ldap');
138         set_config('user_type', 'rfc2307', 'auth_ldap');
139         set_config('contexts', 'ou=users,'.$topdn, 'auth_ldap');
140         set_config('search_sub', $subcontext, 'auth_ldap');
141         set_config('opt_deref', LDAP_DEREF_NEVER, 'auth_ldap');
142         set_config('user_attribute', 'cn', 'auth_ldap');
143         set_config('memberattribute', 'memberuid', 'auth_ldap');
144         set_config('memberattribute_isdn', 0, 'auth_ldap');
145         set_config('coursecreatorcontext', 'cn=creators,'.$topdn, 'auth_ldap');
146         set_config('removeuser', AUTH_REMOVEUSER_KEEP, 'auth_ldap');
148         set_config('field_map_email', 'mail', 'auth_ldap');
149         set_config('field_updatelocal_email', 'oncreate', 'auth_ldap');
150         set_config('field_updateremote_email', '0', 'auth_ldap');
151         set_config('field_lock_email', 'unlocked', 'auth_ldap');
153         set_config('field_map_firstname', 'givenName', 'auth_ldap');
154         set_config('field_updatelocal_firstname', 'oncreate', 'auth_ldap');
155         set_config('field_updateremote_firstname', '0', 'auth_ldap');
156         set_config('field_lock_firstname', 'unlocked', 'auth_ldap');
158         set_config('field_map_lastname', 'sn', 'auth_ldap');
159         set_config('field_updatelocal_lastname', 'oncreate', 'auth_ldap');
160         set_config('field_updateremote_lastname', '0', 'auth_ldap');
161         set_config('field_lock_lastname', 'unlocked', 'auth_ldap');
164         $this->assertEquals(2, $DB->count_records('user'));
165         $this->assertEquals(0, $DB->count_records('role_assignments'));
167         /** @var auth_plugin_ldap $auth */
168         $auth = get_auth_plugin('ldap');
170         ob_start();
171         $sink = $this->redirectEvents();
172         $auth->sync_users(true);
173         $events = $sink->get_events();
174         $sink->close();
175         ob_end_clean();
177         // Check events, 5 users created with 2 users having roles.
178         $this->assertCount(7, $events);
179         foreach ($events as $index => $event) {
180             $username = $DB->get_field('user', 'username', array('id' => $event->relateduserid)); // Get username.
182             if ($event->eventname === '\core\event\user_created') {
183                 $this->assertContains($username, $createdusers);
184                 unset($events[$index]); // Remove matching event.
186             } else if ($event->eventname === '\core\event\role_assigned') {
187                 $this->assertContains($username, $assignedroles);
188                 unset($events[$index]); // Remove matching event.
190             } else {
191                 $this->fail('Unexpected event found: ' . $event->eventname);
192             }
193         }
194         // If all the user_created and role_assigned events have matched
195         // then the $events array should be now empty.
196         $this->assertCount(0, $events);
198         $this->assertEquals(5, $DB->count_records('user', array('auth'=>'ldap')));
199         $this->assertEquals(2, $DB->count_records('role_assignments'));
200         $this->assertEquals(2, $DB->count_records('role_assignments', array('roleid'=>$creatorrole->id)));
202         for ($i=1; $i<=5; $i++) {
203             $this->assertTrue($DB->record_exists('user', array('username'=>'username'.$i, 'email'=>'user'.$i.'@example.com', 'firstname'=>'Firstname'.$i, 'lastname'=>'Lastname'.$i)));
204         }
206         $this->delete_ldap_user($connection, $topdn, 1);
208         ob_start();
209         $sink = $this->redirectEvents();
210         $auth->sync_users(true);
211         $events = $sink->get_events();
212         $sink->close();
213         ob_end_clean();
215         // Check events, no new event.
216         $this->assertCount(0, $events);
218         $this->assertEquals(5, $DB->count_records('user', array('auth'=>'ldap')));
219         $this->assertEquals(0, $DB->count_records('user', array('suspended'=>1)));
220         $this->assertEquals(0, $DB->count_records('user', array('deleted'=>1)));
221         $this->assertEquals(2, $DB->count_records('role_assignments'));
222         $this->assertEquals(2, $DB->count_records('role_assignments', array('roleid'=>$creatorrole->id)));
225         set_config('removeuser', AUTH_REMOVEUSER_SUSPEND, 'auth_ldap');
227         /** @var auth_plugin_ldap $auth */
228         $auth = get_auth_plugin('ldap');
230         ob_start();
231         $sink = $this->redirectEvents();
232         $auth->sync_users(true);
233         $events = $sink->get_events();
234         $sink->close();
235         ob_end_clean();
237         // Check events, 1 user got updated.
238         $this->assertCount(1, $events);
239         $event = reset($events);
240         $this->assertInstanceOf('\core\event\user_updated', $event);
242         $this->assertEquals(5, $DB->count_records('user', array('auth'=>'ldap')));
243         $this->assertEquals(0, $DB->count_records('user', array('auth'=>'nologin', 'username'=>'username1')));
244         $this->assertEquals(1, $DB->count_records('user', array('auth'=>'ldap', 'suspended'=>'1', 'username'=>'username1')));
245         $this->assertEquals(0, $DB->count_records('user', array('deleted'=>1)));
246         $this->assertEquals(2, $DB->count_records('role_assignments'));
247         $this->assertEquals(2, $DB->count_records('role_assignments', array('roleid'=>$creatorrole->id)));
249         $this->create_ldap_user($connection, $topdn, 1);
251         ob_start();
252         $sink = $this->redirectEvents();
253         $auth->sync_users(true);
254         $events = $sink->get_events();
255         $sink->close();
256         ob_end_clean();
258         // Check events, 1 user got updated.
259         $this->assertCount(1, $events);
260         $event = reset($events);
261         $this->assertInstanceOf('\core\event\user_updated', $event);
263         $this->assertEquals(5, $DB->count_records('user', array('auth'=>'ldap')));
264         $this->assertEquals(0, $DB->count_records('user', array('suspended'=>1)));
265         $this->assertEquals(0, $DB->count_records('user', array('deleted'=>1)));
266         $this->assertEquals(2, $DB->count_records('role_assignments'));
267         $this->assertEquals(2, $DB->count_records('role_assignments', array('roleid'=>$creatorrole->id)));
269         $DB->set_field('user', 'auth', 'nologin', array('username'=>'username1'));
271         ob_start();
272         $sink = $this->redirectEvents();
273         $auth->sync_users(true);
274         $events = $sink->get_events();
275         $sink->close();
276         ob_end_clean();
278         // Check events, 1 user got updated.
279         $this->assertCount(1, $events);
280         $event = reset($events);
281         $this->assertInstanceOf('\core\event\user_updated', $event);
283         $this->assertEquals(5, $DB->count_records('user', array('auth'=>'ldap')));
284         $this->assertEquals(0, $DB->count_records('user', array('suspended'=>1)));
285         $this->assertEquals(0, $DB->count_records('user', array('deleted'=>1)));
286         $this->assertEquals(2, $DB->count_records('role_assignments'));
287         $this->assertEquals(2, $DB->count_records('role_assignments', array('roleid'=>$creatorrole->id)));
289         set_config('removeuser', AUTH_REMOVEUSER_FULLDELETE, 'auth_ldap');
291         /** @var auth_plugin_ldap $auth */
292         $auth = get_auth_plugin('ldap');
294         $this->delete_ldap_user($connection, $topdn, 1);
296         ob_start();
297         $sink = $this->redirectEvents();
298         $auth->sync_users(true);
299         $events = $sink->get_events();
300         $sink->close();
301         ob_end_clean();
303         // Check events, 2 events role_unassigned and user_deleted.
304         $this->assertCount(2, $events);
305         $event = array_pop($events);
306         $this->assertInstanceOf('\core\event\user_deleted', $event);
307         $event = array_pop($events);
308         $this->assertInstanceOf('\core\event\role_unassigned', $event);
310         $this->assertEquals(5, $DB->count_records('user', array('auth'=>'ldap')));
311         $this->assertEquals(0, $DB->count_records('user', array('username'=>'username1')));
312         $this->assertEquals(0, $DB->count_records('user', array('suspended'=>1)));
313         $this->assertEquals(1, $DB->count_records('user', array('deleted'=>1)));
314         $this->assertEquals(1, $DB->count_records('role_assignments'));
315         $this->assertEquals(1, $DB->count_records('role_assignments', array('roleid'=>$creatorrole->id)));
317         $this->create_ldap_user($connection, $topdn, 1);
319         ob_start();
320         $sink = $this->redirectEvents();
321         $auth->sync_users(true);
322         $events = $sink->get_events();
323         $sink->close();
324         ob_end_clean();
326         // Check events, 2 events role_assigned and user_created.
327         $this->assertCount(2, $events);
328         $event = array_pop($events);
329         $this->assertInstanceOf('\core\event\role_assigned', $event);
330         $event = array_pop($events);
331         $this->assertInstanceOf('\core\event\user_created', $event);
333         $this->assertEquals(6, $DB->count_records('user', array('auth'=>'ldap')));
334         $this->assertEquals(1, $DB->count_records('user', array('username'=>'username1')));
335         $this->assertEquals(0, $DB->count_records('user', array('suspended'=>1)));
336         $this->assertEquals(1, $DB->count_records('user', array('deleted'=>1)));
337         $this->assertEquals(2, $DB->count_records('role_assignments'));
338         $this->assertEquals(2, $DB->count_records('role_assignments', array('roleid'=>$creatorrole->id)));
341         $this->recursive_delete($connection, TEST_AUTH_LDAP_DOMAIN, 'dc=moodletest');
342         ldap_close($connection);
343     }
345     /**
346      * Test logging in via LDAP calls a user_loggedin event.
347      */
348     public function test_ldap_user_loggedin_event() {
349         global $CFG, $DB, $USER;
351         require_once($CFG->dirroot . '/auth/ldap/auth.php');
353         $this->resetAfterTest();
355         $this->assertFalse(isloggedin());
356         $user = $DB->get_record('user', array('username'=>'admin'));
358         // Note: we are just going to trigger the function that calls the event,
359         // not actually perform a LDAP login, for the sake of sanity.
360         $ldap = new auth_plugin_ldap();
362         // Set the key for the cache flag we want to set which is used by LDAP.
363         set_cache_flag($ldap->pluginconfig . '/ntlmsess', sesskey(), $user->username, AUTH_NTLMTIMEOUT);
365         // We are going to need to set the sesskey as the user's password in order for the LDAP log in to work.
366         update_internal_user_password($user, sesskey());
368         // The function ntlmsso_finish is responsible for triggering the event, so call it directly and catch the event.
369         $sink = $this->redirectEvents();
370         // We need to supress this function call, or else we will get the message "session_regenerate_id(): Cannot
371         // regenerate session id - headers already sent" as the ntlmsso_finish function calls complete_user_login
372         @$ldap->ntlmsso_finish();
373         $events = $sink->get_events();
374         $sink->close();
376         // Check that the event is valid.
377         $this->assertCount(1, $events);
378         $event = reset($events);
379         $this->assertInstanceOf('\core\event\user_loggedin', $event);
380         $this->assertEquals('user', $event->objecttable);
381         $this->assertEquals('2', $event->objectid);
382         $this->assertEquals(context_system::instance()->id, $event->contextid);
383         $expectedlog = array(SITEID, 'user', 'login', 'view.php?id=' . $USER->id . '&course=' . SITEID, $user->id,
384             0, $user->id);
385         $this->assertEventLegacyLogData($expectedlog, $event);
386     }
388     /**
389      * Test logging in via LDAP calls a user_loggedin event.
390      */
391     public function test_ldap_user_signup() {
392         global $CFG, $DB;
394         // User to create.
395         $user = array(
396             'username' => 'usersignuptest1',
397             'password' => 'Moodle2014!',
398             'idnumber' => 'idsignuptest1',
399             'firstname' => 'First Name User Test 1',
400             'lastname' => 'Last Name User Test 1',
401             'middlename' => 'Middle Name User Test 1',
402             'lastnamephonetic' => '最後のお名前のテスト一号',
403             'firstnamephonetic' => 'お名前のテスト一号',
404             'alternatename' => 'Alternate Name User Test 1',
405             'email' => 'usersignuptest1@example.com',
406             'description' => 'This is a description for user 1',
407             'city' => 'Perth',
408             'country' => 'AU',
409             'mnethostid' => $CFG->mnet_localhost_id,
410             'auth' => 'ldap'
411             );
413         if (!extension_loaded('ldap')) {
414             $this->markTestSkipped('LDAP extension is not loaded.');
415         }
417         $this->resetAfterTest();
419         require_once($CFG->dirroot.'/auth/ldap/auth.php');
420         require_once($CFG->libdir.'/ldaplib.php');
422         if (!defined('TEST_AUTH_LDAP_HOST_URL') or !defined('TEST_AUTH_LDAP_BIND_DN') or !defined('TEST_AUTH_LDAP_BIND_PW') or !defined('TEST_AUTH_LDAP_DOMAIN')) {
423             $this->markTestSkipped('External LDAP test server not configured.');
424         }
426         // Make sure we can connect the server.
427         $debuginfo = '';
428         if (!$connection = ldap_connect_moodle(TEST_AUTH_LDAP_HOST_URL, 3, 'rfc2307', TEST_AUTH_LDAP_BIND_DN, TEST_AUTH_LDAP_BIND_PW, LDAP_DEREF_NEVER, $debuginfo, false)) {
429             $this->markTestSkipped('Can not connect to LDAP test server: '.$debuginfo);
430         }
432         $this->enable_plugin();
434         // Create new empty test container.
435         $topdn = 'dc=moodletest,'.TEST_AUTH_LDAP_DOMAIN;
437         $this->recursive_delete($connection, TEST_AUTH_LDAP_DOMAIN, 'dc=moodletest');
439         $o = array();
440         $o['objectClass'] = array('dcObject', 'organizationalUnit');
441         $o['dc']         = 'moodletest';
442         $o['ou']         = 'MOODLETEST';
443         if (!ldap_add($connection, 'dc=moodletest,'.TEST_AUTH_LDAP_DOMAIN, $o)) {
444             $this->markTestSkipped('Can not create test LDAP container.');
445         }
447         // Create a few users.
448         $o = array();
449         $o['objectClass'] = array('organizationalUnit');
450         $o['ou']          = 'users';
451         ldap_add($connection, 'ou='.$o['ou'].','.$topdn, $o);
453         // Configure the plugin a bit.
454         set_config('host_url', TEST_AUTH_LDAP_HOST_URL, 'auth_ldap');
455         set_config('start_tls', 0, 'auth_ldap');
456         set_config('ldap_version', 3, 'auth_ldap');
457         set_config('ldapencoding', 'utf-8', 'auth_ldap');
458         set_config('pagesize', '2', 'auth_ldap');
459         set_config('bind_dn', TEST_AUTH_LDAP_BIND_DN, 'auth_ldap');
460         set_config('bind_pw', TEST_AUTH_LDAP_BIND_PW, 'auth_ldap');
461         set_config('user_type', 'rfc2307', 'auth_ldap');
462         set_config('contexts', 'ou=users,'.$topdn, 'auth_ldap');
463         set_config('search_sub', 0, 'auth_ldap');
464         set_config('opt_deref', LDAP_DEREF_NEVER, 'auth_ldap');
465         set_config('user_attribute', 'cn', 'auth_ldap');
466         set_config('memberattribute', 'memberuid', 'auth_ldap');
467         set_config('memberattribute_isdn', 0, 'auth_ldap');
468         set_config('creators', 'cn=creators,'.$topdn, 'auth_ldap');
469         set_config('removeuser', AUTH_REMOVEUSER_KEEP, 'auth_ldap');
471         set_config('field_map_email', 'mail', 'auth_ldap');
472         set_config('field_updatelocal_email', 'oncreate', 'auth_ldap');
473         set_config('field_updateremote_email', '0', 'auth_ldap');
474         set_config('field_lock_email', 'unlocked', 'auth_ldap');
476         set_config('field_map_firstname', 'givenName', 'auth_ldap');
477         set_config('field_updatelocal_firstname', 'oncreate', 'auth_ldap');
478         set_config('field_updateremote_firstname', '0', 'auth_ldap');
479         set_config('field_lock_firstname', 'unlocked', 'auth_ldap');
481         set_config('field_map_lastname', 'sn', 'auth_ldap');
482         set_config('field_updatelocal_lastname', 'oncreate', 'auth_ldap');
483         set_config('field_updateremote_lastname', '0', 'auth_ldap');
484         set_config('field_lock_lastname', 'unlocked', 'auth_ldap');
485         set_config('passtype', 'md5', 'auth_ldap');
486         set_config('create_context', 'ou=users,'.$topdn, 'auth_ldap');
488         $this->assertEquals(2, $DB->count_records('user'));
489         $this->assertEquals(0, $DB->count_records('role_assignments'));
491         /** @var auth_plugin_ldap $auth */
492         $auth = get_auth_plugin('ldap');
494         $sink = $this->redirectEvents();
495         $mailsink = $this->redirectEmails();
496         $auth->user_signup((object)$user, false);
497         $this->assertEquals(1, $mailsink->count());
498         $events = $sink->get_events();
499         $sink->close();
501         // Verify 2 events get generated.
502         $this->assertCount(2, $events);
504         // Get record from db.
505         $dbuser = $DB->get_record('user', array('username' => $user['username']));
506         $user['id'] = $dbuser->id;
508         // Last event is user_created.
509         $event = array_pop($events);
510         $this->assertInstanceOf('\core\event\user_created', $event);
511         $this->assertEquals($user['id'], $event->objectid);
512         $this->assertEquals('user_created', $event->get_legacy_eventname());
513         $this->assertEquals(context_user::instance($user['id']), $event->get_context());
514         $expectedlogdata = array(SITEID, 'user', 'add', '/view.php?id='.$event->objectid, fullname($dbuser));
515         $this->assertEventLegacyLogData($expectedlogdata, $event);
517         // First event is user_password_updated.
518         $event = array_pop($events);
519         $this->assertInstanceOf('\core\event\user_password_updated', $event);
520         $this->assertEventContextNotUsed($event);
522         // Delete user which we just created.
523         ldap_delete($connection, 'cn='.$user['username'].',ou=users,'.$topdn);
524     }
526     protected function create_ldap_user($connection, $topdn, $i) {
527         $o = array();
528         $o['objectClass']   = array('inetOrgPerson', 'organizationalPerson', 'person', 'posixAccount');
529         $o['cn']            = 'username'.$i;
530         $o['sn']            = 'Lastname'.$i;
531         $o['givenName']     = 'Firstname'.$i;
532         $o['uid']           = $o['cn'];
533         $o['uidnumber']     = 2000+$i;
534         $o['gidNumber']     = 1000+$i;
535         $o['homeDirectory'] = '/';
536         $o['mail']          = 'user'.$i.'@example.com';
537         $o['userPassword']  = 'pass'.$i;
538         ldap_add($connection, 'cn='.$o['cn'].',ou=users,'.$topdn, $o);
539     }
541     protected function delete_ldap_user($connection, $topdn, $i) {
542         ldap_delete($connection, 'cn=username'.$i.',ou=users,'.$topdn);
543     }
545     protected function enable_plugin() {
546         $auths = get_enabled_auth_plugins(true);
547         if (!in_array('ldap', $auths)) {
548             $auths[] = 'ldap';
550         }
551         set_config('auth', implode(',', $auths));
552     }
554     protected function recursive_delete($connection, $dn, $filter) {
555         if ($res = ldap_list($connection, $dn, $filter, array('dn'))) {
556             $info = ldap_get_entries($connection, $res);
557             ldap_free_result($res);
558             if ($info['count'] > 0) {
559                 if ($res = ldap_search($connection, "$filter,$dn", 'cn=*', array('dn'))) {
560                     $info = ldap_get_entries($connection, $res);
561                     ldap_free_result($res);
562                     foreach ($info as $i) {
563                         if (isset($i['dn'])) {
564                             ldap_delete($connection, $i['dn']);
565                         }
566                     }
567                 }
568                 if ($res = ldap_search($connection, "$filter,$dn", 'ou=*', array('dn'))) {
569                     $info = ldap_get_entries($connection, $res);
570                     ldap_free_result($res);
571                     foreach ($info as $i) {
572                         if (isset($i['dn']) and $info[0]['dn'] != $i['dn']) {
573                             ldap_delete($connection, $i['dn']);
574                         }
575                     }
576                 }
577                 ldap_delete($connection, "$filter,$dn");
578             }
579         }
580     }