MDL-49022 auth_ldap: trigger event when required.
[moodle.git] / auth / ldap / tests / plugin_test.php
CommitLineData
a7aff74f
PS
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 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 */
34
35defined('MOODLE_INTERNAL') || die();
36
d03e4508 37class auth_ldap_plugin_testcase extends advanced_testcase {
a7aff74f
PS
38
39 public function test_auth_ldap() {
40 global $CFG, $DB;
41
42 if (!extension_loaded('ldap')) {
43 $this->markTestSkipped('LDAP extension is not loaded.');
44 }
45
46 $this->resetAfterTest();
47
48 require_once($CFG->dirroot.'/auth/ldap/auth.php');
49 require_once($CFG->libdir.'/ldaplib.php');
50
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 }
54
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)) {
be094a59 58 $this->markTestSkipped('Can not connect to LDAP test server: '.$debuginfo);
a7aff74f
PS
59 }
60
61 $this->enable_plugin();
62
63 // Create new empty test container.
64 $topdn = 'dc=moodletest,'.TEST_AUTH_LDAP_DOMAIN;
65
66 $this->recursive_delete($connection, TEST_AUTH_LDAP_DOMAIN, 'dc=moodletest');
67
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 }
75
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);
81
82 for ($i=1; $i<=5; $i++) {
83 $this->create_ldap_user($connection, $topdn, $i);
84 }
85
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);
93
94 $creatorrole = $DB->get_record('role', array('shortname'=>'coursecreator'));
95 $this->assertNotEmpty($creatorrole);
96
97
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('creators', 'cn=creators,'.$topdn, 'auth/ldap');
114 set_config('removeuser', AUTH_REMOVEUSER_KEEP, 'auth/ldap');
115
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');
120
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');
125
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');
130
131
132 $this->assertEquals(2, $DB->count_records('user'));
133 $this->assertEquals(0, $DB->count_records('role_assignments'));
134
135 /** @var auth_plugin_ldap $auth */
136 $auth = get_auth_plugin('ldap');
137
138 ob_start();
139 $auth->sync_users(true);
140 ob_end_clean();
141
142 $this->assertEquals(5, $DB->count_records('user', array('auth'=>'ldap')));
143 $this->assertEquals(2, $DB->count_records('role_assignments'));
144 $this->assertEquals(2, $DB->count_records('role_assignments', array('roleid'=>$creatorrole->id)));
145
146 for ($i=1; $i<=5; $i++) {
147 $this->assertTrue($DB->record_exists('user', array('username'=>'username'.$i, 'email'=>'user'.$i.'@example.com', 'firstname'=>'Firstname'.$i, 'lastname'=>'Lastname'.$i)));
148 }
149
150 $this->delete_ldap_user($connection, $topdn, 1);
151
152 ob_start();
153 $auth->sync_users(true);
154 ob_end_clean();
155
156 $this->assertEquals(5, $DB->count_records('user', array('auth'=>'ldap')));
157 $this->assertEquals(0, $DB->count_records('user', array('suspended'=>1)));
158 $this->assertEquals(0, $DB->count_records('user', array('deleted'=>1)));
159 $this->assertEquals(2, $DB->count_records('role_assignments'));
160 $this->assertEquals(2, $DB->count_records('role_assignments', array('roleid'=>$creatorrole->id)));
161
162
163 set_config('removeuser', AUTH_REMOVEUSER_SUSPEND, 'auth/ldap');
164
165 /** @var auth_plugin_ldap $auth */
166 $auth = get_auth_plugin('ldap');
167
168 ob_start();
169 $auth->sync_users(true);
170 ob_end_clean();
171
d03e4508
PS
172 $this->assertEquals(5, $DB->count_records('user', array('auth'=>'ldap')));
173 $this->assertEquals(0, $DB->count_records('user', array('auth'=>'nologin', 'username'=>'username1')));
174 $this->assertEquals(1, $DB->count_records('user', array('auth'=>'ldap', 'suspended'=>'1', 'username'=>'username1')));
a7aff74f
PS
175 $this->assertEquals(0, $DB->count_records('user', array('deleted'=>1)));
176 $this->assertEquals(2, $DB->count_records('role_assignments'));
177 $this->assertEquals(2, $DB->count_records('role_assignments', array('roleid'=>$creatorrole->id)));
178
179 $this->create_ldap_user($connection, $topdn, 1);
180
181 ob_start();
182 $auth->sync_users(true);
183 ob_end_clean();
184
185 $this->assertEquals(5, $DB->count_records('user', array('auth'=>'ldap')));
186 $this->assertEquals(0, $DB->count_records('user', array('suspended'=>1)));
187 $this->assertEquals(0, $DB->count_records('user', array('deleted'=>1)));
188 $this->assertEquals(2, $DB->count_records('role_assignments'));
189 $this->assertEquals(2, $DB->count_records('role_assignments', array('roleid'=>$creatorrole->id)));
190
d03e4508
PS
191 $DB->set_field('user', 'auth', 'nologin', array('username'=>'username1'));
192
193 ob_start();
194 $auth->sync_users(true);
195 ob_end_clean();
196
197 $this->assertEquals(5, $DB->count_records('user', array('auth'=>'ldap')));
198 $this->assertEquals(0, $DB->count_records('user', array('suspended'=>1)));
199 $this->assertEquals(0, $DB->count_records('user', array('deleted'=>1)));
200 $this->assertEquals(2, $DB->count_records('role_assignments'));
201 $this->assertEquals(2, $DB->count_records('role_assignments', array('roleid'=>$creatorrole->id)));
a7aff74f
PS
202
203 set_config('removeuser', AUTH_REMOVEUSER_FULLDELETE, 'auth/ldap');
204
205 /** @var auth_plugin_ldap $auth */
206 $auth = get_auth_plugin('ldap');
207
208 $this->delete_ldap_user($connection, $topdn, 1);
209
210 ob_start();
211 $auth->sync_users(true);
212 ob_end_clean();
213
214 $this->assertEquals(5, $DB->count_records('user', array('auth'=>'ldap')));
215 $this->assertEquals(0, $DB->count_records('user', array('username'=>'username1')));
216 $this->assertEquals(0, $DB->count_records('user', array('suspended'=>1)));
217 $this->assertEquals(1, $DB->count_records('user', array('deleted'=>1)));
218 $this->assertEquals(1, $DB->count_records('role_assignments'));
219 $this->assertEquals(1, $DB->count_records('role_assignments', array('roleid'=>$creatorrole->id)));
220
221 $this->create_ldap_user($connection, $topdn, 1);
222
223 ob_start();
224 $auth->sync_users(true);
225 ob_end_clean();
226
227 $this->assertEquals(6, $DB->count_records('user', array('auth'=>'ldap')));
228 $this->assertEquals(1, $DB->count_records('user', array('username'=>'username1')));
229 $this->assertEquals(0, $DB->count_records('user', array('suspended'=>1)));
230 $this->assertEquals(1, $DB->count_records('user', array('deleted'=>1)));
231 $this->assertEquals(2, $DB->count_records('role_assignments'));
232 $this->assertEquals(2, $DB->count_records('role_assignments', array('roleid'=>$creatorrole->id)));
233
234
235 $this->recursive_delete($connection, TEST_AUTH_LDAP_DOMAIN, 'dc=moodletest');
236 ldap_close($connection);
237 }
238
c8974473
MN
239 /**
240 * Test logging in via LDAP calls a user_loggedin event.
241 */
242 public function test_ldap_user_loggedin_event() {
243 global $CFG, $DB, $USER;
244
245 require_once($CFG->dirroot . '/auth/ldap/auth.php');
246
247 $this->resetAfterTest();
248
93b3aaf4
PS
249 $this->assertFalse(isloggedin());
250 $user = $DB->get_record('user', array('username'=>'admin'));
c8974473
MN
251
252 // Note: we are just going to trigger the function that calls the event,
253 // not actually perform a LDAP login, for the sake of sanity.
254 $ldap = new auth_plugin_ldap();
255
256 // Set the key for the cache flag we want to set which is used by LDAP.
257 set_cache_flag($ldap->pluginconfig . '/ntlmsess', sesskey(), $user->username, AUTH_NTLMTIMEOUT);
258
259 // We are going to need to set the sesskey as the user's password in order for the LDAP log in to work.
260 update_internal_user_password($user, sesskey());
261
262 // The function ntlmsso_finish is responsible for triggering the event, so call it directly and catch the event.
263 $sink = $this->redirectEvents();
264 // We need to supress this function call, or else we will get the message "session_regenerate_id(): Cannot
265 // regenerate session id - headers already sent" as the ntlmsso_finish function calls complete_user_login
266 @$ldap->ntlmsso_finish();
267 $events = $sink->get_events();
268 $sink->close();
269
c8974473 270 // Check that the event is valid.
54b1e040
271 $this->assertCount(1, $events);
272 $event = reset($events);
c8974473
MN
273 $this->assertInstanceOf('\core\event\user_loggedin', $event);
274 $this->assertEquals('user', $event->objecttable);
275 $this->assertEquals('2', $event->objectid);
276 $this->assertEquals(context_system::instance()->id, $event->contextid);
c8974473
MN
277 $expectedlog = array(SITEID, 'user', 'login', 'view.php?id=' . $USER->id . '&course=' . SITEID, $user->id,
278 0, $user->id);
279 $this->assertEventLegacyLogData($expectedlog, $event);
280 }
281
f1259a76
RT
282 /**
283 * Test logging in via LDAP calls a user_loggedin event.
284 */
285 public function test_ldap_user_signup() {
286 global $CFG, $DB;
287
288 // User to create.
289 $user = array(
290 'username' => 'usersignuptest1',
291 'password' => 'Moodle2014!',
292 'idnumber' => 'idsignuptest1',
293 'firstname' => 'First Name User Test 1',
294 'lastname' => 'Last Name User Test 1',
295 'middlename' => 'Middle Name User Test 1',
296 'lastnamephonetic' => '最後のお名前のテスト一号',
297 'firstnamephonetic' => 'お名前のテスト一号',
298 'alternatename' => 'Alternate Name User Test 1',
0fe86bbd 299 'email' => 'usersignuptest1@example.com',
f1259a76
RT
300 'description' => 'This is a description for user 1',
301 'city' => 'Perth',
302 'country' => 'au',
303 'mnethostid' => $CFG->mnet_localhost_id,
304 'auth' => 'ldap'
305 );
306
307 if (!extension_loaded('ldap')) {
308 $this->markTestSkipped('LDAP extension is not loaded.');
309 }
310
311 $this->resetAfterTest();
312
313 require_once($CFG->dirroot.'/auth/ldap/auth.php');
314 require_once($CFG->libdir.'/ldaplib.php');
315
316 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')) {
317 $this->markTestSkipped('External LDAP test server not configured.');
318 }
319
320 // Make sure we can connect the server.
321 $debuginfo = '';
322 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)) {
323 $this->markTestSkipped('Can not connect to LDAP test server: '.$debuginfo);
324 }
325
326 $this->enable_plugin();
327
328 // Create new empty test container.
329 $topdn = 'dc=moodletest,'.TEST_AUTH_LDAP_DOMAIN;
330
331 $this->recursive_delete($connection, TEST_AUTH_LDAP_DOMAIN, 'dc=moodletest');
332
333 $o = array();
334 $o['objectClass'] = array('dcObject', 'organizationalUnit');
335 $o['dc'] = 'moodletest';
336 $o['ou'] = 'MOODLETEST';
337 if (!ldap_add($connection, 'dc=moodletest,'.TEST_AUTH_LDAP_DOMAIN, $o)) {
338 $this->markTestSkipped('Can not create test LDAP container.');
339 }
340
341 // Create a few users.
342 $o = array();
343 $o['objectClass'] = array('organizationalUnit');
344 $o['ou'] = 'users';
345 ldap_add($connection, 'ou='.$o['ou'].','.$topdn, $o);
346
347 // Configure the plugin a bit.
348 set_config('host_url', TEST_AUTH_LDAP_HOST_URL, 'auth/ldap');
349 set_config('start_tls', 0, 'auth/ldap');
350 set_config('ldap_version', 3, 'auth/ldap');
351 set_config('ldapencoding', 'utf-8', 'auth/ldap');
352 set_config('pagesize', '2', 'auth/ldap');
353 set_config('bind_dn', TEST_AUTH_LDAP_BIND_DN, 'auth/ldap');
354 set_config('bind_pw', TEST_AUTH_LDAP_BIND_PW, 'auth/ldap');
355 set_config('user_type', 'rfc2307', 'auth/ldap');
356 set_config('contexts', 'ou=users,'.$topdn, 'auth/ldap');
357 set_config('search_sub', 0, 'auth/ldap');
358 set_config('opt_deref', LDAP_DEREF_NEVER, 'auth/ldap');
359 set_config('user_attribute', 'cn', 'auth/ldap');
360 set_config('memberattribute', 'memberuid', 'auth/ldap');
361 set_config('memberattribute_isdn', 0, 'auth/ldap');
362 set_config('creators', 'cn=creators,'.$topdn, 'auth/ldap');
363 set_config('removeuser', AUTH_REMOVEUSER_KEEP, 'auth/ldap');
364
365 set_config('field_map_email', 'mail', 'auth/ldap');
366 set_config('field_updatelocal_email', 'oncreate', 'auth/ldap');
367 set_config('field_updateremote_email', '0', 'auth/ldap');
368 set_config('field_lock_email', 'unlocked', 'auth/ldap');
369
370 set_config('field_map_firstname', 'givenName', 'auth/ldap');
371 set_config('field_updatelocal_firstname', 'oncreate', 'auth/ldap');
372 set_config('field_updateremote_firstname', '0', 'auth/ldap');
373 set_config('field_lock_firstname', 'unlocked', 'auth/ldap');
374
375 set_config('field_map_lastname', 'sn', 'auth/ldap');
376 set_config('field_updatelocal_lastname', 'oncreate', 'auth/ldap');
377 set_config('field_updateremote_lastname', '0', 'auth/ldap');
378 set_config('field_lock_lastname', 'unlocked', 'auth/ldap');
379 set_config('passtype', 'md5', 'auth/ldap');
380 set_config('create_context', 'ou=users,'.$topdn, 'auth/ldap');
381
382 $this->assertEquals(2, $DB->count_records('user'));
383 $this->assertEquals(0, $DB->count_records('role_assignments'));
384
385 /** @var auth_plugin_ldap $auth */
386 $auth = get_auth_plugin('ldap');
387
388 $sink = $this->redirectEvents();
fc05c1b8 389 $mailsink = $this->redirectEmails();
f1259a76 390 $auth->user_signup((object)$user, false);
fc05c1b8 391 $this->assertEquals(1, $mailsink->count());
f1259a76
RT
392 $events = $sink->get_events();
393 $sink->close();
394
395 // Verify 2 events get generated.
396 $this->assertCount(2, $events);
397
398 // Get record from db.
399 $dbuser = $DB->get_record('user', array('username' => $user['username']));
400 $user['id'] = $dbuser->id;
401
402 // Last event is user_created.
403 $event = array_pop($events);
404 $this->assertInstanceOf('\core\event\user_created', $event);
405 $this->assertEquals($user['id'], $event->objectid);
406 $this->assertEquals('user_created', $event->get_legacy_eventname());
407 $this->assertEquals(context_user::instance($user['id']), $event->get_context());
408 $expectedlogdata = array(SITEID, 'user', 'add', '/view.php?id='.$event->objectid, fullname($dbuser));
409 $this->assertEventLegacyLogData($expectedlogdata, $event);
410
411 // First event is user_password_updated.
412 $event = array_pop($events);
413 $this->assertInstanceOf('\core\event\user_password_updated', $event);
414 $this->assertEventContextNotUsed($event);
415
416 // Delete user which we just created.
417 ldap_delete($connection, 'cn='.$user['username'].',ou=users,'.$topdn);
418 }
419
a7aff74f
PS
420 protected function create_ldap_user($connection, $topdn, $i) {
421 $o = array();
422 $o['objectClass'] = array('inetOrgPerson', 'organizationalPerson', 'person', 'posixAccount');
423 $o['cn'] = 'username'.$i;
424 $o['sn'] = 'Lastname'.$i;
425 $o['givenName'] = 'Firstname'.$i;
426 $o['uid'] = $o['cn'];
427 $o['uidnumber'] = 2000+$i;
428 $o['gidNumber'] = 1000+$i;
429 $o['homeDirectory'] = '/';
430 $o['mail'] = 'user'.$i.'@example.com';
431 $o['userPassword'] = 'pass'.$i;
432 ldap_add($connection, 'cn='.$o['cn'].',ou=users,'.$topdn, $o);
433 }
434
435 protected function delete_ldap_user($connection, $topdn, $i) {
436 ldap_delete($connection, 'cn=username'.$i.',ou=users,'.$topdn);
437 }
438
439 protected function enable_plugin() {
440 $auths = get_enabled_auth_plugins(true);
441 if (!in_array('ldap', $auths)) {
442 $auths[] = 'ldap';
443
444 }
445 set_config('auth', implode(',', $auths));
446 }
447
448 protected function recursive_delete($connection, $dn, $filter) {
449 if ($res = ldap_list($connection, $dn, $filter, array('dn'))) {
450 $info = ldap_get_entries($connection, $res);
451 ldap_free_result($res);
452 if ($info['count'] > 0) {
453 if ($res = ldap_search($connection, "$filter,$dn", 'cn=*', array('dn'))) {
454 $info = ldap_get_entries($connection, $res);
455 ldap_free_result($res);
456 foreach ($info as $i) {
457 if (isset($i['dn'])) {
458 ldap_delete($connection, $i['dn']);
459 }
460 }
461 }
462 if ($res = ldap_search($connection, "$filter,$dn", 'ou=*', array('dn'))) {
463 $info = ldap_get_entries($connection, $res);
464 ldap_free_result($res);
465 foreach ($info as $i) {
466 if (isset($i['dn']) and $info[0]['dn'] != $i['dn']) {
467 ldap_delete($connection, $i['dn']);
468 }
469 }
470 }
471 ldap_delete($connection, "$filter,$dn");
472 }
473 }
474 }
475}