MDL-59734 auth_ldap: Fix role sync
[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     public function test_auth_ldap() {
40         global $CFG, $DB;
42         if (!extension_loaded('ldap')) {
43             $this->markTestSkipped('LDAP extension is not loaded.');
44         }
46         $this->resetAfterTest();
48         require_once($CFG->dirroot.'/auth/ldap/auth.php');
49         require_once($CFG->libdir.'/ldaplib.php');
51         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')) {
52             $this->markTestSkipped('External LDAP test server not configured.');
53         }
55         // Make sure we can connect the server.
56         $debuginfo = '';
57         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)) {
58             $this->markTestSkipped('Can not connect to LDAP test server: '.$debuginfo);
59         }
61         $this->enable_plugin();
63         // Create new empty test container.
64         $topdn = 'dc=moodletest,'.TEST_AUTH_LDAP_DOMAIN;
66         $this->recursive_delete($connection, TEST_AUTH_LDAP_DOMAIN, 'dc=moodletest');
68         $o = array();
69         $o['objectClass'] = array('dcObject', 'organizationalUnit');
70         $o['dc']         = 'moodletest';
71         $o['ou']         = 'MOODLETEST';
72         if (!ldap_add($connection, 'dc=moodletest,'.TEST_AUTH_LDAP_DOMAIN, $o)) {
73             $this->markTestSkipped('Can not create test LDAP container.');
74         }
76         // Create a few users.
77         $o = array();
78         $o['objectClass'] = array('organizationalUnit');
79         $o['ou']          = 'users';
80         ldap_add($connection, 'ou='.$o['ou'].','.$topdn, $o);
82         for ($i=1; $i<=5; $i++) {
83             $this->create_ldap_user($connection, $topdn, $i);
84         }
86         // Set up creators group.
87         $o = array();
88         $o['objectClass'] = array('posixGroup');
89         $o['cn']          = 'creators';
90         $o['gidNumber']   = 1;
91         $o['memberUid']   = array('username1', 'username2');
92         ldap_add($connection, 'cn='.$o['cn'].','.$topdn, $o);
94         $creatorrole = $DB->get_record('role', array('shortname'=>'coursecreator'));
95         $this->assertNotEmpty($creatorrole);
98         // Configure the plugin a bit.
99         set_config('host_url', TEST_AUTH_LDAP_HOST_URL, 'auth_ldap');
100         set_config('start_tls', 0, 'auth_ldap');
101         set_config('ldap_version', 3, 'auth_ldap');
102         set_config('ldapencoding', 'utf-8', 'auth_ldap');
103         set_config('pagesize', '2', 'auth_ldap');
104         set_config('bind_dn', TEST_AUTH_LDAP_BIND_DN, 'auth_ldap');
105         set_config('bind_pw', TEST_AUTH_LDAP_BIND_PW, 'auth_ldap');
106         set_config('user_type', 'rfc2307', 'auth_ldap');
107         set_config('contexts', 'ou=users,'.$topdn, 'auth_ldap');
108         set_config('search_sub', 0, 'auth_ldap');
109         set_config('opt_deref', LDAP_DEREF_NEVER, 'auth_ldap');
110         set_config('user_attribute', 'cn', 'auth_ldap');
111         set_config('memberattribute', 'memberuid', 'auth_ldap');
112         set_config('memberattribute_isdn', 0, 'auth_ldap');
113         set_config('coursecreatorcontext', 'cn=creators,'.$topdn, 'auth_ldap');
114         set_config('removeuser', AUTH_REMOVEUSER_KEEP, 'auth_ldap');
116         set_config('field_map_email', 'mail', 'auth_ldap');
117         set_config('field_updatelocal_email', 'oncreate', 'auth_ldap');
118         set_config('field_updateremote_email', '0', 'auth_ldap');
119         set_config('field_lock_email', 'unlocked', 'auth_ldap');
121         set_config('field_map_firstname', 'givenName', 'auth_ldap');
122         set_config('field_updatelocal_firstname', 'oncreate', 'auth_ldap');
123         set_config('field_updateremote_firstname', '0', 'auth_ldap');
124         set_config('field_lock_firstname', 'unlocked', 'auth_ldap');
126         set_config('field_map_lastname', 'sn', 'auth_ldap');
127         set_config('field_updatelocal_lastname', 'oncreate', 'auth_ldap');
128         set_config('field_updateremote_lastname', '0', 'auth_ldap');
129         set_config('field_lock_lastname', 'unlocked', 'auth_ldap');
132         $this->assertEquals(2, $DB->count_records('user'));
133         $this->assertEquals(0, $DB->count_records('role_assignments'));
135         /** @var auth_plugin_ldap $auth */
136         $auth = get_auth_plugin('ldap');
138         ob_start();
139         $sink = $this->redirectEvents();
140         $auth->sync_users(true);
141         $events = $sink->get_events();
142         $sink->close();
143         ob_end_clean();
145         // Check events, 5 users created with 2 users having roles.
146         $this->assertCount(7, $events);
147         foreach ($events as $index => $event) {
148             $usercreatedindex = array(0, 2, 4, 5, 6);
149             $roleassignedindex = array (1, 3);
150             if (in_array($index, $usercreatedindex)) {
151                 $this->assertInstanceOf('\core\event\user_created', $event);
152             }
153             if (in_array($index, $roleassignedindex)) {
154                 $this->assertInstanceOf('\core\event\role_assigned', $event);
155             }
156         }
158         $this->assertEquals(5, $DB->count_records('user', array('auth'=>'ldap')));
159         $this->assertEquals(2, $DB->count_records('role_assignments'));
160         $this->assertEquals(2, $DB->count_records('role_assignments', array('roleid'=>$creatorrole->id)));
162         for ($i=1; $i<=5; $i++) {
163             $this->assertTrue($DB->record_exists('user', array('username'=>'username'.$i, 'email'=>'user'.$i.'@example.com', 'firstname'=>'Firstname'.$i, 'lastname'=>'Lastname'.$i)));
164         }
166         $this->delete_ldap_user($connection, $topdn, 1);
168         ob_start();
169         $sink = $this->redirectEvents();
170         $auth->sync_users(true);
171         $events = $sink->get_events();
172         $sink->close();
173         ob_end_clean();
175         // Check events, no new event.
176         $this->assertCount(0, $events);
178         $this->assertEquals(5, $DB->count_records('user', array('auth'=>'ldap')));
179         $this->assertEquals(0, $DB->count_records('user', array('suspended'=>1)));
180         $this->assertEquals(0, $DB->count_records('user', array('deleted'=>1)));
181         $this->assertEquals(2, $DB->count_records('role_assignments'));
182         $this->assertEquals(2, $DB->count_records('role_assignments', array('roleid'=>$creatorrole->id)));
185         set_config('removeuser', AUTH_REMOVEUSER_SUSPEND, 'auth_ldap');
187         /** @var auth_plugin_ldap $auth */
188         $auth = get_auth_plugin('ldap');
190         ob_start();
191         $sink = $this->redirectEvents();
192         $auth->sync_users(true);
193         $events = $sink->get_events();
194         $sink->close();
195         ob_end_clean();
197         // Check events, 1 user got updated.
198         $this->assertCount(1, $events);
199         $event = reset($events);
200         $this->assertInstanceOf('\core\event\user_updated', $event);
202         $this->assertEquals(5, $DB->count_records('user', array('auth'=>'ldap')));
203         $this->assertEquals(0, $DB->count_records('user', array('auth'=>'nologin', 'username'=>'username1')));
204         $this->assertEquals(1, $DB->count_records('user', array('auth'=>'ldap', 'suspended'=>'1', 'username'=>'username1')));
205         $this->assertEquals(0, $DB->count_records('user', array('deleted'=>1)));
206         $this->assertEquals(2, $DB->count_records('role_assignments'));
207         $this->assertEquals(2, $DB->count_records('role_assignments', array('roleid'=>$creatorrole->id)));
209         $this->create_ldap_user($connection, $topdn, 1);
211         ob_start();
212         $sink = $this->redirectEvents();
213         $auth->sync_users(true);
214         $events = $sink->get_events();
215         $sink->close();
216         ob_end_clean();
218         // Check events, 1 user got updated.
219         $this->assertCount(1, $events);
220         $event = reset($events);
221         $this->assertInstanceOf('\core\event\user_updated', $event);
223         $this->assertEquals(5, $DB->count_records('user', array('auth'=>'ldap')));
224         $this->assertEquals(0, $DB->count_records('user', array('suspended'=>1)));
225         $this->assertEquals(0, $DB->count_records('user', array('deleted'=>1)));
226         $this->assertEquals(2, $DB->count_records('role_assignments'));
227         $this->assertEquals(2, $DB->count_records('role_assignments', array('roleid'=>$creatorrole->id)));
229         $DB->set_field('user', 'auth', 'nologin', array('username'=>'username1'));
231         ob_start();
232         $sink = $this->redirectEvents();
233         $auth->sync_users(true);
234         $events = $sink->get_events();
235         $sink->close();
236         ob_end_clean();
238         // Check events, 1 user got updated.
239         $this->assertCount(1, $events);
240         $event = reset($events);
241         $this->assertInstanceOf('\core\event\user_updated', $event);
243         $this->assertEquals(5, $DB->count_records('user', array('auth'=>'ldap')));
244         $this->assertEquals(0, $DB->count_records('user', array('suspended'=>1)));
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         set_config('removeuser', AUTH_REMOVEUSER_FULLDELETE, 'auth_ldap');
251         /** @var auth_plugin_ldap $auth */
252         $auth = get_auth_plugin('ldap');
254         $this->delete_ldap_user($connection, $topdn, 1);
256         ob_start();
257         $sink = $this->redirectEvents();
258         $auth->sync_users(true);
259         $events = $sink->get_events();
260         $sink->close();
261         ob_end_clean();
263         // Check events, 2 events role_unassigned and user_deleted.
264         $this->assertCount(2, $events);
265         $event = array_pop($events);
266         $this->assertInstanceOf('\core\event\user_deleted', $event);
267         $event = array_pop($events);
268         $this->assertInstanceOf('\core\event\role_unassigned', $event);
270         $this->assertEquals(5, $DB->count_records('user', array('auth'=>'ldap')));
271         $this->assertEquals(0, $DB->count_records('user', array('username'=>'username1')));
272         $this->assertEquals(0, $DB->count_records('user', array('suspended'=>1)));
273         $this->assertEquals(1, $DB->count_records('user', array('deleted'=>1)));
274         $this->assertEquals(1, $DB->count_records('role_assignments'));
275         $this->assertEquals(1, $DB->count_records('role_assignments', array('roleid'=>$creatorrole->id)));
277         $this->create_ldap_user($connection, $topdn, 1);
279         ob_start();
280         $sink = $this->redirectEvents();
281         $auth->sync_users(true);
282         $events = $sink->get_events();
283         $sink->close();
284         ob_end_clean();
286         // Check events, 2 events role_assigned and user_created.
287         $this->assertCount(2, $events);
288         $event = array_pop($events);
289         $this->assertInstanceOf('\core\event\role_assigned', $event);
290         $event = array_pop($events);
291         $this->assertInstanceOf('\core\event\user_created', $event);
293         $this->assertEquals(6, $DB->count_records('user', array('auth'=>'ldap')));
294         $this->assertEquals(1, $DB->count_records('user', array('username'=>'username1')));
295         $this->assertEquals(0, $DB->count_records('user', array('suspended'=>1)));
296         $this->assertEquals(1, $DB->count_records('user', array('deleted'=>1)));
297         $this->assertEquals(2, $DB->count_records('role_assignments'));
298         $this->assertEquals(2, $DB->count_records('role_assignments', array('roleid'=>$creatorrole->id)));
301         $this->recursive_delete($connection, TEST_AUTH_LDAP_DOMAIN, 'dc=moodletest');
302         ldap_close($connection);
303     }
305     /**
306      * Test logging in via LDAP calls a user_loggedin event.
307      */
308     public function test_ldap_user_loggedin_event() {
309         global $CFG, $DB, $USER;
311         require_once($CFG->dirroot . '/auth/ldap/auth.php');
313         $this->resetAfterTest();
315         $this->assertFalse(isloggedin());
316         $user = $DB->get_record('user', array('username'=>'admin'));
318         // Note: we are just going to trigger the function that calls the event,
319         // not actually perform a LDAP login, for the sake of sanity.
320         $ldap = new auth_plugin_ldap();
322         // Set the key for the cache flag we want to set which is used by LDAP.
323         set_cache_flag($ldap->pluginconfig . '/ntlmsess', sesskey(), $user->username, AUTH_NTLMTIMEOUT);
325         // We are going to need to set the sesskey as the user's password in order for the LDAP log in to work.
326         update_internal_user_password($user, sesskey());
328         // The function ntlmsso_finish is responsible for triggering the event, so call it directly and catch the event.
329         $sink = $this->redirectEvents();
330         // We need to supress this function call, or else we will get the message "session_regenerate_id(): Cannot
331         // regenerate session id - headers already sent" as the ntlmsso_finish function calls complete_user_login
332         @$ldap->ntlmsso_finish();
333         $events = $sink->get_events();
334         $sink->close();
336         // Check that the event is valid.
337         $this->assertCount(1, $events);
338         $event = reset($events);
339         $this->assertInstanceOf('\core\event\user_loggedin', $event);
340         $this->assertEquals('user', $event->objecttable);
341         $this->assertEquals('2', $event->objectid);
342         $this->assertEquals(context_system::instance()->id, $event->contextid);
343         $expectedlog = array(SITEID, 'user', 'login', 'view.php?id=' . $USER->id . '&course=' . SITEID, $user->id,
344             0, $user->id);
345         $this->assertEventLegacyLogData($expectedlog, $event);
346     }
348     /**
349      * Test logging in via LDAP calls a user_loggedin event.
350      */
351     public function test_ldap_user_signup() {
352         global $CFG, $DB;
354         // User to create.
355         $user = array(
356             'username' => 'usersignuptest1',
357             'password' => 'Moodle2014!',
358             'idnumber' => 'idsignuptest1',
359             'firstname' => 'First Name User Test 1',
360             'lastname' => 'Last Name User Test 1',
361             'middlename' => 'Middle Name User Test 1',
362             'lastnamephonetic' => '最後のお名前のテスト一号',
363             'firstnamephonetic' => 'お名前のテスト一号',
364             'alternatename' => 'Alternate Name User Test 1',
365             'email' => 'usersignuptest1@example.com',
366             'description' => 'This is a description for user 1',
367             'city' => 'Perth',
368             'country' => 'AU',
369             'mnethostid' => $CFG->mnet_localhost_id,
370             'auth' => 'ldap'
371             );
373         if (!extension_loaded('ldap')) {
374             $this->markTestSkipped('LDAP extension is not loaded.');
375         }
377         $this->resetAfterTest();
379         require_once($CFG->dirroot.'/auth/ldap/auth.php');
380         require_once($CFG->libdir.'/ldaplib.php');
382         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')) {
383             $this->markTestSkipped('External LDAP test server not configured.');
384         }
386         // Make sure we can connect the server.
387         $debuginfo = '';
388         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)) {
389             $this->markTestSkipped('Can not connect to LDAP test server: '.$debuginfo);
390         }
392         $this->enable_plugin();
394         // Create new empty test container.
395         $topdn = 'dc=moodletest,'.TEST_AUTH_LDAP_DOMAIN;
397         $this->recursive_delete($connection, TEST_AUTH_LDAP_DOMAIN, 'dc=moodletest');
399         $o = array();
400         $o['objectClass'] = array('dcObject', 'organizationalUnit');
401         $o['dc']         = 'moodletest';
402         $o['ou']         = 'MOODLETEST';
403         if (!ldap_add($connection, 'dc=moodletest,'.TEST_AUTH_LDAP_DOMAIN, $o)) {
404             $this->markTestSkipped('Can not create test LDAP container.');
405         }
407         // Create a few users.
408         $o = array();
409         $o['objectClass'] = array('organizationalUnit');
410         $o['ou']          = 'users';
411         ldap_add($connection, 'ou='.$o['ou'].','.$topdn, $o);
413         // Configure the plugin a bit.
414         set_config('host_url', TEST_AUTH_LDAP_HOST_URL, 'auth_ldap');
415         set_config('start_tls', 0, 'auth_ldap');
416         set_config('ldap_version', 3, 'auth_ldap');
417         set_config('ldapencoding', 'utf-8', 'auth_ldap');
418         set_config('pagesize', '2', 'auth_ldap');
419         set_config('bind_dn', TEST_AUTH_LDAP_BIND_DN, 'auth_ldap');
420         set_config('bind_pw', TEST_AUTH_LDAP_BIND_PW, 'auth_ldap');
421         set_config('user_type', 'rfc2307', 'auth_ldap');
422         set_config('contexts', 'ou=users,'.$topdn, 'auth_ldap');
423         set_config('search_sub', 0, 'auth_ldap');
424         set_config('opt_deref', LDAP_DEREF_NEVER, 'auth_ldap');
425         set_config('user_attribute', 'cn', 'auth_ldap');
426         set_config('memberattribute', 'memberuid', 'auth_ldap');
427         set_config('memberattribute_isdn', 0, 'auth_ldap');
428         set_config('creators', 'cn=creators,'.$topdn, 'auth_ldap');
429         set_config('removeuser', AUTH_REMOVEUSER_KEEP, 'auth_ldap');
431         set_config('field_map_email', 'mail', 'auth_ldap');
432         set_config('field_updatelocal_email', 'oncreate', 'auth_ldap');
433         set_config('field_updateremote_email', '0', 'auth_ldap');
434         set_config('field_lock_email', 'unlocked', 'auth_ldap');
436         set_config('field_map_firstname', 'givenName', 'auth_ldap');
437         set_config('field_updatelocal_firstname', 'oncreate', 'auth_ldap');
438         set_config('field_updateremote_firstname', '0', 'auth_ldap');
439         set_config('field_lock_firstname', 'unlocked', 'auth_ldap');
441         set_config('field_map_lastname', 'sn', 'auth_ldap');
442         set_config('field_updatelocal_lastname', 'oncreate', 'auth_ldap');
443         set_config('field_updateremote_lastname', '0', 'auth_ldap');
444         set_config('field_lock_lastname', 'unlocked', 'auth_ldap');
445         set_config('passtype', 'md5', 'auth_ldap');
446         set_config('create_context', 'ou=users,'.$topdn, 'auth_ldap');
448         $this->assertEquals(2, $DB->count_records('user'));
449         $this->assertEquals(0, $DB->count_records('role_assignments'));
451         /** @var auth_plugin_ldap $auth */
452         $auth = get_auth_plugin('ldap');
454         $sink = $this->redirectEvents();
455         $mailsink = $this->redirectEmails();
456         $auth->user_signup((object)$user, false);
457         $this->assertEquals(1, $mailsink->count());
458         $events = $sink->get_events();
459         $sink->close();
461         // Verify 2 events get generated.
462         $this->assertCount(2, $events);
464         // Get record from db.
465         $dbuser = $DB->get_record('user', array('username' => $user['username']));
466         $user['id'] = $dbuser->id;
468         // Last event is user_created.
469         $event = array_pop($events);
470         $this->assertInstanceOf('\core\event\user_created', $event);
471         $this->assertEquals($user['id'], $event->objectid);
472         $this->assertEquals('user_created', $event->get_legacy_eventname());
473         $this->assertEquals(context_user::instance($user['id']), $event->get_context());
474         $expectedlogdata = array(SITEID, 'user', 'add', '/view.php?id='.$event->objectid, fullname($dbuser));
475         $this->assertEventLegacyLogData($expectedlogdata, $event);
477         // First event is user_password_updated.
478         $event = array_pop($events);
479         $this->assertInstanceOf('\core\event\user_password_updated', $event);
480         $this->assertEventContextNotUsed($event);
482         // Delete user which we just created.
483         ldap_delete($connection, 'cn='.$user['username'].',ou=users,'.$topdn);
484     }
486     protected function create_ldap_user($connection, $topdn, $i) {
487         $o = array();
488         $o['objectClass']   = array('inetOrgPerson', 'organizationalPerson', 'person', 'posixAccount');
489         $o['cn']            = 'username'.$i;
490         $o['sn']            = 'Lastname'.$i;
491         $o['givenName']     = 'Firstname'.$i;
492         $o['uid']           = $o['cn'];
493         $o['uidnumber']     = 2000+$i;
494         $o['gidNumber']     = 1000+$i;
495         $o['homeDirectory'] = '/';
496         $o['mail']          = 'user'.$i.'@example.com';
497         $o['userPassword']  = 'pass'.$i;
498         ldap_add($connection, 'cn='.$o['cn'].',ou=users,'.$topdn, $o);
499     }
501     protected function delete_ldap_user($connection, $topdn, $i) {
502         ldap_delete($connection, 'cn=username'.$i.',ou=users,'.$topdn);
503     }
505     protected function enable_plugin() {
506         $auths = get_enabled_auth_plugins(true);
507         if (!in_array('ldap', $auths)) {
508             $auths[] = 'ldap';
510         }
511         set_config('auth', implode(',', $auths));
512     }
514     protected function recursive_delete($connection, $dn, $filter) {
515         if ($res = ldap_list($connection, $dn, $filter, array('dn'))) {
516             $info = ldap_get_entries($connection, $res);
517             ldap_free_result($res);
518             if ($info['count'] > 0) {
519                 if ($res = ldap_search($connection, "$filter,$dn", 'cn=*', array('dn'))) {
520                     $info = ldap_get_entries($connection, $res);
521                     ldap_free_result($res);
522                     foreach ($info as $i) {
523                         if (isset($i['dn'])) {
524                             ldap_delete($connection, $i['dn']);
525                         }
526                     }
527                 }
528                 if ($res = ldap_search($connection, "$filter,$dn", 'ou=*', array('dn'))) {
529                     $info = ldap_get_entries($connection, $res);
530                     ldap_free_result($res);
531                     foreach ($info as $i) {
532                         if (isset($i['dn']) and $info[0]['dn'] != $i['dn']) {
533                             ldap_delete($connection, $i['dn']);
534                         }
535                     }
536                 }
537                 ldap_delete($connection, "$filter,$dn");
538             }
539         }
540     }