2 // This file is part of Moodle - http://moodle.org/
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.
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.
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/>.
18 * Authentication related tests.
22 * @copyright 2012 Petr Skoda {@link http://skodak.org}
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26 defined('MOODLE_INTERNAL') ||
die();
30 * Functional test for authentication related APIs.
32 class core_authlib_testcase
extends advanced_testcase
{
33 public function test_lockout() {
35 require_once("$CFG->libdir/authlib.php");
37 $this->resetAfterTest();
39 $oldlog = ini_get('error_log');
40 ini_set('error_log', "$CFG->dataroot/testlog.log"); // Prevent standard logging.
42 unset_config('noemailever');
44 set_config('lockoutthreshold', 0);
45 set_config('lockoutwindow', 60*20);
46 set_config('lockoutduration', 60*30);
48 $user = $this->getDataGenerator()->create_user();
50 // Test lockout is disabled when threshold not set.
52 $this->assertFalse(login_is_lockedout($user));
53 login_attempt_failed($user);
54 login_attempt_failed($user);
55 login_attempt_failed($user);
56 login_attempt_failed($user);
57 $this->assertFalse(login_is_lockedout($user));
59 // Test lockout threshold works.
61 set_config('lockoutthreshold', 3);
62 login_attempt_failed($user);
63 login_attempt_failed($user);
64 $this->assertFalse(login_is_lockedout($user));
65 $sink = $this->redirectEmails();
66 login_attempt_failed($user);
67 $this->assertCount(1, $sink->get_messages());
69 $this->assertTrue(login_is_lockedout($user));
73 login_unlock_account($user);
74 $this->assertFalse(login_is_lockedout($user));
76 // Test lockout window works.
78 login_attempt_failed($user);
79 login_attempt_failed($user);
80 $this->assertFalse(login_is_lockedout($user));
81 set_user_preference('login_failed_last', time()-60*20-10, $user);
82 login_attempt_failed($user);
83 $this->assertFalse(login_is_lockedout($user));
85 // Test valid login resets window.
87 login_attempt_valid($user);
88 $this->assertFalse(login_is_lockedout($user));
89 login_attempt_failed($user);
90 login_attempt_failed($user);
91 $this->assertFalse(login_is_lockedout($user));
93 // Test lock duration works.
95 $sink = $this->redirectEmails();
96 login_attempt_failed($user);
97 $this->assertCount(1, $sink->get_messages());
99 $this->assertTrue(login_is_lockedout($user));
100 set_user_preference('login_lockout', time()-60*30+
10, $user);
101 $this->assertTrue(login_is_lockedout($user));
102 set_user_preference('login_lockout', time()-60*30-10, $user);
103 $this->assertFalse(login_is_lockedout($user));
105 // Test lockout ignored pref works.
107 set_user_preference('login_lockout_ignored', 1, $user);
108 login_attempt_failed($user);
109 login_attempt_failed($user);
110 login_attempt_failed($user);
111 login_attempt_failed($user);
112 $this->assertFalse(login_is_lockedout($user));
114 ini_set('error_log', $oldlog);
117 public function test_authenticate_user_login() {
120 $this->resetAfterTest();
122 $oldlog = ini_get('error_log');
123 ini_set('error_log', "$CFG->dataroot/testlog.log"); // Prevent standard logging.
125 unset_config('noemailever');
127 set_config('lockoutthreshold', 0);
128 set_config('lockoutwindow', 60*20);
129 set_config('lockoutduration', 60*30);
131 $_SERVER['HTTP_USER_AGENT'] = 'no browser'; // Hack around missing user agent in CLI scripts.
133 $user1 = $this->getDataGenerator()->create_user(array('username'=>'username1', 'password'=>'password1', 'email'=>'email1@example.com'));
134 $user2 = $this->getDataGenerator()->create_user(array('username'=>'username2', 'password'=>'password2', 'email'=>'email2@example.com', 'suspended'=>1));
135 $user3 = $this->getDataGenerator()->create_user(array('username'=>'username3', 'password'=>'password3', 'email'=>'email2@example.com', 'auth'=>'nologin'));
138 $sink = $this->redirectEvents();
139 $result = authenticate_user_login('username1', 'password1');
140 $events = $sink->get_events();
142 $this->assertEmpty($events);
143 $this->assertInstanceOf('stdClass', $result);
144 $this->assertEquals($user1->id
, $result->id
);
146 // Normal login with reason.
148 $sink = $this->redirectEvents();
149 $result = authenticate_user_login('username1', 'password1', false, $reason);
150 $events = $sink->get_events();
152 $this->assertEmpty($events);
153 $this->assertInstanceOf('stdClass', $result);
154 $this->assertEquals(AUTH_LOGIN_OK
, $reason);
156 // Test login via email
158 $this->assertEmpty($CFG->authloginviaemail
);
159 $sink = $this->redirectEvents();
160 $result = authenticate_user_login('email1@example.com', 'password1', false, $reason);
162 $this->assertFalse($result);
163 $this->assertEquals(AUTH_LOGIN_NOUSER
, $reason);
165 set_config('authloginviaemail', 1);
166 $this->assertNotEmpty($CFG->authloginviaemail
);
167 $sink = $this->redirectEvents();
168 $result = authenticate_user_login('email1@example.com', 'password1');
169 $events = $sink->get_events();
171 $this->assertEmpty($events);
172 $this->assertInstanceOf('stdClass', $result);
173 $this->assertEquals($user1->id
, $result->id
);
176 $sink = $this->redirectEvents();
177 $result = authenticate_user_login('email2@example.com', 'password2', false, $reason);
178 $events = $sink->get_events();
180 $this->assertFalse($result);
181 $this->assertEquals(AUTH_LOGIN_NOUSER
, $reason);
182 set_config('authloginviaemail', 0);
185 // Capture failed login event.
186 $sink = $this->redirectEvents();
187 $result = authenticate_user_login('username1', 'nopass', false, $reason);
188 $events = $sink->get_events();
190 $event = array_pop($events);
192 $this->assertFalse($result);
193 $this->assertEquals(AUTH_LOGIN_FAILED
, $reason);
195 $this->assertInstanceOf('\core\event\user_login_failed', $event);
196 $expectedlogdata = array(SITEID
, 'login', 'error', 'index.php', 'username1');
197 $this->assertEventLegacyLogData($expectedlogdata, $event);
198 $eventdata = $event->get_data();
199 $this->assertSame($eventdata['other']['username'], 'username1');
200 $this->assertSame($eventdata['other']['reason'], AUTH_LOGIN_FAILED
);
201 $this->assertEventContextNotUsed($event);
204 // Capture failed login event.
205 $sink = $this->redirectEvents();
206 $result = authenticate_user_login('username2', 'password2', false, $reason);
207 $events = $sink->get_events();
209 $event = array_pop($events);
211 $this->assertFalse($result);
212 $this->assertEquals(AUTH_LOGIN_SUSPENDED
, $reason);
214 $this->assertInstanceOf('\core\event\user_login_failed', $event);
215 $expectedlogdata = array(SITEID
, 'login', 'error', 'index.php', 'username2');
216 $this->assertEventLegacyLogData($expectedlogdata, $event);
217 $eventdata = $event->get_data();
218 $this->assertSame($eventdata['other']['username'], 'username2');
219 $this->assertSame($eventdata['other']['reason'], AUTH_LOGIN_SUSPENDED
);
220 $this->assertEventContextNotUsed($event);
223 // Capture failed login event.
224 $sink = $this->redirectEvents();
225 $result = authenticate_user_login('username3', 'password3', false, $reason);
226 $events = $sink->get_events();
228 $event = array_pop($events);
230 $this->assertFalse($result);
231 $this->assertEquals(AUTH_LOGIN_SUSPENDED
, $reason);
233 $this->assertInstanceOf('\core\event\user_login_failed', $event);
234 $expectedlogdata = array(SITEID
, 'login', 'error', 'index.php', 'username3');
235 $this->assertEventLegacyLogData($expectedlogdata, $event);
236 $eventdata = $event->get_data();
237 $this->assertSame($eventdata['other']['username'], 'username3');
238 $this->assertSame($eventdata['other']['reason'], AUTH_LOGIN_SUSPENDED
);
239 $this->assertEventContextNotUsed($event);
242 // Capture failed login event.
243 $sink = $this->redirectEvents();
244 $result = authenticate_user_login('username4', 'password3', false, $reason);
245 $events = $sink->get_events();
247 $event = array_pop($events);
249 $this->assertFalse($result);
250 $this->assertEquals(AUTH_LOGIN_NOUSER
, $reason);
252 $this->assertInstanceOf('\core\event\user_login_failed', $event);
253 $expectedlogdata = array(SITEID
, 'login', 'error', 'index.php', 'username4');
254 $this->assertEventLegacyLogData($expectedlogdata, $event);
255 $eventdata = $event->get_data();
256 $this->assertSame($eventdata['other']['username'], 'username4');
257 $this->assertSame($eventdata['other']['reason'], AUTH_LOGIN_NOUSER
);
258 $this->assertEventContextNotUsed($event);
260 set_config('lockoutthreshold', 3);
263 $result = authenticate_user_login('username1', 'nopass', false, $reason);
264 $this->assertFalse($result);
265 $this->assertEquals(AUTH_LOGIN_FAILED
, $reason);
266 $result = authenticate_user_login('username1', 'nopass', false, $reason);
267 $this->assertFalse($result);
268 $this->assertEquals(AUTH_LOGIN_FAILED
, $reason);
269 $sink = $this->redirectEmails();
270 $result = authenticate_user_login('username1', 'nopass', false, $reason);
271 $this->assertCount(1, $sink->get_messages());
273 $this->assertFalse($result);
274 $this->assertEquals(AUTH_LOGIN_FAILED
, $reason);
276 $result = authenticate_user_login('username1', 'password1', false, $reason);
277 $this->assertFalse($result);
278 $this->assertEquals(AUTH_LOGIN_LOCKOUT
, $reason);
280 $result = authenticate_user_login('username1', 'password1', true, $reason);
281 $this->assertInstanceOf('stdClass', $result);
282 $this->assertEquals(AUTH_LOGIN_OK
, $reason);
284 ini_set('error_log', $oldlog);
287 public function test_user_loggedin_event_exceptions() {
289 $event = \core\event\user_loggedin
::create(array('objectid' => 1));
290 $this->fail('\core\event\user_loggedin requires other[\'username\']');
291 } catch(Exception
$e) {
292 $this->assertInstanceOf('coding_exception', $e);