MDL-48887 An auth plugin hook enabling removal of redundant redirects
[moodle.git] / lib / authlib.php
CommitLineData
72dbdcea 1<?php
2
117bd748
PS
3// This file is part of Moodle - http://moodle.org/
4//
72dbdcea 5// Moodle is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// Moodle is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
117bd748 14//
72dbdcea 15// You should have received a copy of the GNU General Public License
16// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
17
6bc1e5d5 18/**
72dbdcea 19 * Multiple plugin authentication Support library
6bc1e5d5 20 *
21 * 2006-08-28 File created, AUTH return values defined.
117bd748 22 *
78bfb562
PS
23 * @package core
24 * @subpackage auth
25 * @copyright 1999 onwards Martin Dougiamas http://dougiamas.com
26 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6bc1e5d5 27 */
28
78bfb562
PS
29defined('MOODLE_INTERNAL') || die();
30
6bc1e5d5 31/**
32 * Returned when the login was successful.
33 */
34define('AUTH_OK', 0);
35
36/**
37 * Returned when the login was unsuccessful.
38 */
39define('AUTH_FAIL', 1);
40
41/**
42 * Returned when the login was denied (a reason for AUTH_FAIL).
43 */
44define('AUTH_DENIED', 2);
45
46/**
47 * Returned when some error occurred (a reason for AUTH_FAIL).
48 */
49define('AUTH_ERROR', 4);
50
51/**
52 * Authentication - error codes for user confirm
53 */
54define('AUTH_CONFIRM_FAIL', 0);
55define('AUTH_CONFIRM_OK', 1);
56define('AUTH_CONFIRM_ALREADY', 2);
57define('AUTH_CONFIRM_ERROR', 3);
58
6f87ef52 59# MDL-14055
60define('AUTH_REMOVEUSER_KEEP', 0);
61define('AUTH_REMOVEUSER_SUSPEND', 1);
62define('AUTH_REMOVEUSER_FULLDELETE', 2);
6bc1e5d5 63
b28247fe
PS
64/** Login attempt successful. */
65define('AUTH_LOGIN_OK', 0);
66
67/** Can not login because user does not exist. */
68define('AUTH_LOGIN_NOUSER', 1);
69
70/** Can not login because user is suspended. */
71define('AUTH_LOGIN_SUSPENDED', 2);
72
73/** Can not login, most probably password did not match. */
74define('AUTH_LOGIN_FAILED', 3);
75
76/** Can not login because user is locked out. */
77define('AUTH_LOGIN_LOCKOUT', 4);
78
65d7932c
CF
79/** Can not login becauser user is not authorised. */
80define('AUTH_LOGIN_UNAUTHORISED', 5);
b28247fe 81
6bc1e5d5 82/**
83 * Abstract authentication plugin.
72dbdcea 84 *
85 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
86 * @package moodlecore
6bc1e5d5 87 */
88class auth_plugin_base {
89
90 /**
91 * The configuration details for the plugin.
72dbdcea 92 * @var object
6bc1e5d5 93 */
94 var $config;
95
96 /**
97 * Authentication plugin type - the same as db field.
72dbdcea 98 * @var string
6bc1e5d5 99 */
100 var $authtype;
4105caff 101 /*
102 * The fields we can lock and update from/to external authentication backends
72dbdcea 103 * @var array
4105caff 104 */
5891220d 105 var $userfields = array(
106 'firstname',
107 'lastname',
108 'email',
109 'city',
110 'country',
111 'lang',
112 'description',
113 'url',
114 'idnumber',
115 'institution',
116 'department',
117 'phone1',
118 'phone2',
2fbd261d
VD
119 'address',
120 'firstnamephonetic',
121 'lastnamephonetic',
122 'middlename',
123 'alternatename'
5891220d 124 );
6bc1e5d5 125
57d135a1
RT
126 /**
127 * Moodle custom fields to sync with.
128 * @var array()
129 */
d836e3ed 130 var $customfields = null;
57d135a1 131
6bc1e5d5 132 /**
f5fd4347 133 * This is the primary method that is used by the authenticate_user_login()
117bd748 134 * function in moodlelib.php.
72dbdcea 135 *
136 * This method should return a boolean indicating
f5fd4347 137 * whether or not the username and password authenticate successfully.
138 *
6bc1e5d5 139 * Returns true if the username and password work and false if they are
140 * wrong or don't exist.
141 *
142 * @param string $username The username (with system magic quotes)
143 * @param string $password The password (with system magic quotes)
144 *
145 * @return bool Authentication success or failure.
146 */
147 function user_login($username, $password) {
2f137aa1 148 print_error('mustbeoveride', 'debug', '', 'user_login()' );
6bc1e5d5 149 }
150
151 /**
f5fd4347 152 * Returns true if this authentication plugin can change the users'
6bc1e5d5 153 * password.
154 *
155 * @return bool
156 */
157 function can_change_password() {
158 //override if needed
159 return false;
160 }
161
162 /**
f5fd4347 163 * Returns the URL for changing the users' passwords, or empty if the default
117bd748 164 * URL can be used.
72dbdcea 165 *
166 * This method is used if can_change_password() returns true.
80274abf 167 * This method is called only when user is logged in, it may use global $USER.
963cdce4
AA
168 * If you are using a plugin config variable in this method, please make sure it is set before using it,
169 * as this method can be called even if the plugin is disabled, in which case the config values won't be set.
6bc1e5d5 170 *
99f9f85f 171 * @return moodle_url url of the profile page or null if standard used
6bc1e5d5 172 */
173 function change_password_url() {
174 //override if needed
99f9f85f
PS
175 return null;
176 }
177
178 /**
179 * Returns true if this authentication plugin can edit the users'
180 * profile.
181 *
182 * @return bool
183 */
184 function can_edit_profile() {
185 //override if needed
186 return true;
187 }
188
189 /**
190 * Returns the URL for editing the users' profile, or empty if the default
191 * URL can be used.
192 *
193 * This method is used if can_edit_profile() returns true.
194 * This method is called only when user is logged in, it may use global $USER.
195 *
196 * @return moodle_url url of the profile page or null if standard used
197 */
198 function edit_profile_url() {
199 //override if needed
200 return null;
6bc1e5d5 201 }
202
203 /**
7415aed1
PS
204 * Returns true if this authentication plugin is "internal".
205 *
206 * Internal plugins use password hashes from Moodle user table for authentication.
6bc1e5d5 207 *
208 * @return bool
209 */
210 function is_internal() {
211 //override if needed
212 return true;
213 }
214
edb5da83
PS
215 /**
216 * Indicates if password hashes should be stored in local moodle database.
217 * @return bool true means md5 password hash stored in user table, false means flag 'not_cached' stored there instead
218 */
219 function prevent_local_passwords() {
7415aed1
PS
220 return !$this->is_internal();
221 }
222
223 /**
224 * Indicates if moodle should automatically update internal user
225 * records with data from external sources using the information
226 * from get_userinfo() method.
227 *
228 * @return bool true means automatically copy data from ext to user table
229 */
230 function is_synchronised_with_external() {
231 return !$this->is_internal();
edb5da83
PS
232 }
233
6bc1e5d5 234 /**
117bd748 235 * Updates the user's password.
72dbdcea 236 *
237 * In previous versions of Moodle, the function
f5fd4347 238 * auth_user_update_password accepted a username as the first parameter. The
239 * revised function expects a user object.
6bc1e5d5 240 *
ae040d4b 241 * @param object $user User table object
242 * @param string $newpassword Plaintext password
6bc1e5d5 243 *
244 * @return bool True on success
245 */
246 function user_update_password($user, $newpassword) {
247 //override if needed
248 return true;
249 }
250
251 /**
252 * Called when the user record is updated.
253 * Modifies user in external database. It takes olduser (before changes) and newuser (after changes)
640df5d0 254 * compares information saved modified information to external db.
6bc1e5d5 255 *
256 * @param mixed $olduser Userobject before modifications (without system magic quotes)
257 * @param mixed $newuser Userobject new modified userobject (without system magic quotes)
258 * @return boolean true if updated or update ignored; false if error
259 *
260 */
261 function user_update($olduser, $newuser) {
262 //override if needed
263 return true;
264 }
265
90afcf32 266 /**
267 * User delete requested - internal user record is mared as deleted already, username not present anymore.
72dbdcea 268 *
90afcf32 269 * Do any action in external database.
72dbdcea 270 *
90afcf32 271 * @param object $user Userobject before delete (without system magic quotes)
72dbdcea 272 * @return void
90afcf32 273 */
274 function user_delete($olduser) {
275 //override if needed
276 return;
277 }
278
6bc1e5d5 279 /**
280 * Returns true if plugin allows resetting of internal password.
281 *
282 * @return bool
283 */
284 function can_reset_password() {
285 //override if needed
286 return false;
287 }
288
289 /**
290 * Returns true if plugin allows resetting of internal password.
291 *
292 * @return bool
293 */
294 function can_signup() {
295 //override if needed
296 return false;
297 }
298
299 /**
300 * Sign up a new user ready for confirmation.
301 * Password is passed in plaintext.
302 *
5d910388 303 * @param object $user new user object
6bc1e5d5 304 * @param boolean $notify print notice with link and terminate
305 */
306 function user_signup($user, $notify=true) {
307 //override when can signup
2f137aa1 308 print_error('mustbeoveride', 'debug', '', 'user_signup()' );
6bc1e5d5 309 }
b217edd2 310
57d38adc 311 /**
b217edd2 312 * Return a form to capture user details for account creation.
57d38adc
MA
313 * This is used in /login/signup.php.
314 * @return moodle_form A form which edits a record from the user table.
315 */
316 function signup_form() {
317 global $CFG;
b217edd2 318
57d38adc
MA
319 require_once($CFG->dirroot.'/login/signup_form.php');
320 return new login_signup_form(null, null, 'post', '', array('autocomplete'=>'on'));
321 }
6bc1e5d5 322
323 /**
324 * Returns true if plugin allows confirming of new users.
325 *
326 * @return bool
327 */
328 function can_confirm() {
329 //override if needed
330 return false;
331 }
332
333 /**
334 * Confirm the new user as registered.
335 *
b9a66360 336 * @param string $username
337 * @param string $confirmsecret
6bc1e5d5 338 */
339 function user_confirm($username, $confirmsecret) {
340 //override when can confirm
2f137aa1 341 print_error('mustbeoveride', 'debug', '', 'user_confirm()' );
6bc1e5d5 342 }
343
344 /**
345 * Checks if user exists in external db
346 *
347 * @param string $username (with system magic quotes)
03cedd62 348 * @return bool
6bc1e5d5 349 */
d61ffb0f 350 function user_exists($username) {
6bc1e5d5 351 //override if needed
352 return false;
353 }
354
6bc1e5d5 355 /**
356 * return number of days to user password expires
357 *
358 * If userpassword does not expire it should return 0. If password is already expired
359 * it should return negative value.
360 *
361 * @param mixed $username username (with system magic quotes)
362 * @return integer
363 */
364 function password_expire($username) {
365 return 0;
366 }
367 /**
368 * Sync roles for this user - usually creator
369 *
370 * @param $user object user object (without system magic quotes)
371 */
372 function sync_roles($user) {
373 //override if needed
374 }
375
376 /**
377 * Read user information from external database and returns it as array().
378 * Function should return all information available. If you are saving
640df5d0 379 * this information to moodle user-table you should honour synchronisation flags
6bc1e5d5 380 *
be544ec3 381 * @param string $username username
6bc1e5d5 382 *
383 * @return mixed array with no magic quotes or false on error
384 */
385 function get_userinfo($username) {
386 //override if needed
387 return array();
388 }
389
f5fd4347 390 /**
391 * Prints a form for configuring this authentication plugin.
392 *
393 * This function is called from admin/auth.php, and outputs a full page with
394 * a form for configuring this plugin.
72dbdcea 395 *
396 * @param object $config
397 * @param object $err
398 * @param array $user_fields
f5fd4347 399 */
400 function config_form($config, $err, $user_fields) {
401 //override if needed
402 }
403
6bc1e5d5 404 /**
405 * A chance to validate form data, and last chance to
406 * do stuff before it is inserted in config_plugin
f4f2b8fb 407 * @param object object with submitted configuration settings (without system magic quotes)
408 * @param array $err array of error messages
6bc1e5d5 409 */
e2bb3c92 410 function validate_form($form, &$err) {
6bc1e5d5 411 //override if needed
412 }
413
414 /**
f5fd4347 415 * Processes and stores configuration data for this authentication plugin.
f4f2b8fb 416 *
417 * @param object object with submitted configuration settings (without system magic quotes)
6bc1e5d5 418 */
f5fd4347 419 function process_config($config) {
420 //override if needed
421 return true;
422 }
423
424 /**
640df5d0 425 * Hook for overriding behaviour of login page.
f5fd4347 426 * This method is called from login/index.php page for all enabled auth plugins.
72dbdcea 427 *
428 * @global object
429 * @global object
f5fd4347 430 */
431 function loginpage_hook() {
432 global $frm; // can be used to override submitted login form
433 global $user; // can be used to replace authenticate_user_login()
434
6bc1e5d5 435 //override if needed
436 }
437
bf08e3f9
BH
438 /**
439 * Hook for overriding behaviour before going to the login page.
440 *
441 * This method is called from require_login from potentially any page for
442 * all enabled auth plugins and gives each plugin a chance to redirect
443 * directly to an external login page, or to instantly login a user where
444 * possible.
445 *
446 * If an auth plugin implements this hook, it must not rely on ONLY this
447 * hook in order to work, as there are many ways a user can browse directly
448 * to the standard login page. As a general rule in this case you should
449 * also implement the loginpage_hook as well.
450 *
451 */
452 function pre_loginpage_hook() {
453 // override if needed, eg by redirecting to an external login page
454 // or logging in a user:
455 // complete_user_login($user);
456 }
457
6bc1e5d5 458 /**
459 * Post authentication hook.
f5fd4347 460 * This method is called from authenticate_user_login() for all enabled auth plugins.
461 *
462 * @param object $user user object, later used for $USER
463 * @param string $username (with system magic quotes)
464 * @param string $password plain text password (with system magic quotes)
6bc1e5d5 465 */
f5fd4347 466 function user_authenticated_hook(&$user, $username, $password) {
467 //override if needed
6bc1e5d5 468 }
469
470 /**
f5fd4347 471 * Pre logout hook.
472 * This method is called from require_logout() for all enabled auth plugins,
72dbdcea 473 *
474 * @global object
6bc1e5d5 475 */
476 function prelogout_hook() {
f5fd4347 477 global $USER; // use $USER->auth to find the plugin used for login
478
479 //override if needed
480 }
481
482 /**
640df5d0 483 * Hook for overriding behaviour of logout page.
f5fd4347 484 * This method is called from login/logout.php page for all enabled auth plugins.
72dbdcea 485 *
486 * @global object
487 * @global string
f5fd4347 488 */
489 function logoutpage_hook() {
490 global $USER; // use $USER->auth to find the plugin used for login
491 global $redirect; // can be used to override redirect after logout
492
6bc1e5d5 493 //override if needed
494 }
2a5f4a88 495
e8656bef 496 /**
497 * Hook called before timing out of database session.
640df5d0 498 * This is useful for SSO and MNET.
72dbdcea 499 *
e8656bef 500 * @param object $user
501 * @param string $sid session id
502 * @param int $timecreated start of session
503 * @param int $timemodified user last seen
504 * @return bool true means do not timeout session yet
505 */
506 function ignore_timeout_hook($user, $sid, $timecreated, $timemodified) {
88fdd846 507 return false;
508 }
509
2a5f4a88 510 /**
511 * Return the properly translated human-friendly title of this auth plugin
72dbdcea 512 *
513 * @todo Document this function
2a5f4a88 514 */
515 function get_title() {
370f10b7 516 return get_string('pluginname', "auth_{$this->authtype}");
2a5f4a88 517 }
518
519 /**
72dbdcea 520 * Get the auth description (from core or own auth lang files)
521 *
522 * @return string The description
2a5f4a88 523 */
524 function get_description() {
37f005b1 525 $authdescription = get_string("auth_{$this->authtype}description", "auth_{$this->authtype}");
2a5f4a88 526 return $authdescription;
527 }
117bd748 528
9b5f87d2 529 /**
530 * Returns whether or not the captcha element is enabled, and the admin settings fulfil its requirements.
72dbdcea 531 *
9b5f87d2 532 * @abstract Implement in child classes
533 * @return bool
534 */
535 function is_captcha_enabled() {
536 return false;
537 }
538
9b29f686
MN
539 /**
540 * Returns whether or not this authentication plugin can be manually set
541 * for users, for example, when bulk uploading users.
542 *
543 * This should be overriden by authentication plugins where setting the
544 * authentication method manually is allowed.
545 *
546 * @return bool
5bcfd504 547 * @since Moodle 2.6
9b29f686
MN
548 */
549 function can_be_manually_set() {
550 // Override if needed.
551 return false;
552 }
553
b257d7c4
PL
554 /**
555 * Returns a list of potential IdPs that this authentication plugin supports.
556 * This is used to provide links on the login page.
557 *
558 * @param string $wantsurl the relative url fragment the user wants to get to. You can use this to compose a returnurl, for example
559 *
560 * @return array like:
561 * array(
562 * array(
563 * 'url' => 'http://someurl',
564 * 'icon' => new pix_icon(...),
565 * 'name' => get_string('somename', 'auth_yourplugin'),
566 * ),
567 * )
568 */
569 function loginpage_idp_list($wantsurl) {
570 return array();
571 }
9b5f87d2 572
57d135a1
RT
573 /**
574 * Return custom user profile fields.
575 *
576 * @return array list of custom fields.
577 */
578 public function get_custom_user_profile_fields() {
579 global $DB;
d836e3ed
RT
580 // If already retrieved then return.
581 if (!is_null($this->customfields)) {
582 return $this->customfields;
57d135a1
RT
583 }
584
d836e3ed 585 $this->customfields = array();
57d135a1
RT
586 if ($proffields = $DB->get_records('user_info_field')) {
587 foreach ($proffields as $proffield) {
d836e3ed 588 $this->customfields[] = 'profile_field_'.$proffield->shortname;
57d135a1
RT
589 }
590 }
591 unset($proffields);
57d135a1 592
d836e3ed 593 return $this->customfields;
57d135a1 594 }
f9f9d187
SL
595
596 /**
597 * Post logout hook.
598 *
599 * This method is used after moodle logout by auth classes to execute server logout.
600 *
601 * @param stdClass $user clone of USER object before the user session was terminated
602 */
603 public function postlogout_hook($user) {
604 }
6bc1e5d5 605}
b28247fe
PS
606
607/**
608 * Verify if user is locked out.
609 *
610 * @param stdClass $user
611 * @return bool true if user locked out
612 */
613function login_is_lockedout($user) {
614 global $CFG;
615
616 if ($user->mnethostid != $CFG->mnet_localhost_id) {
617 return false;
618 }
619 if (isguestuser($user)) {
620 return false;
621 }
622
623 if (empty($CFG->lockoutthreshold)) {
624 // Lockout not enabled.
625 return false;
626 }
627
628 if (get_user_preferences('login_lockout_ignored', 0, $user)) {
629 // This preference may be used for accounts that must not be locked out.
630 return false;
631 }
632
633 $locked = get_user_preferences('login_lockout', 0, $user);
634 if (!$locked) {
635 return false;
636 }
637
638 if (empty($CFG->lockoutduration)) {
639 // Locked out forever.
640 return true;
641 }
642
643 if (time() - $locked < $CFG->lockoutduration) {
644 return true;
645 }
646
647 login_unlock_account($user);
648
649 return false;
650}
651
652/**
653 * To be called after valid user login.
654 * @param stdClass $user
655 */
656function login_attempt_valid($user) {
657 global $CFG;
658
d79d5ac2 659 // Note: user_loggedin event is triggered in complete_user_login().
3b086e30 660
b28247fe
PS
661 if ($user->mnethostid != $CFG->mnet_localhost_id) {
662 return;
663 }
664 if (isguestuser($user)) {
665 return;
666 }
667
668 // Always unlock here, there might be some race conditions or leftovers when switching threshold.
669 login_unlock_account($user);
670}
671
672/**
673 * To be called after failed user login.
674 * @param stdClass $user
675 */
676function login_attempt_failed($user) {
677 global $CFG;
678
679 if ($user->mnethostid != $CFG->mnet_localhost_id) {
680 return;
681 }
682 if (isguestuser($user)) {
683 return;
684 }
685
52dc1de7
AA
686 $count = get_user_preferences('login_failed_count', 0, $user);
687 $last = get_user_preferences('login_failed_last', 0, $user);
688 $sincescuccess = get_user_preferences('login_failed_count_since_success', $count, $user);
689 $sincescuccess = $sincescuccess + 1;
690 set_user_preference('login_failed_count_since_success', $sincescuccess, $user);
691
b28247fe
PS
692 if (empty($CFG->lockoutthreshold)) {
693 // No threshold means no lockout.
694 // Always unlock here, there might be some race conditions or leftovers when switching threshold.
695 login_unlock_account($user);
696 return;
697 }
698
b28247fe
PS
699 if (!empty($CFG->lockoutwindow) and time() - $last > $CFG->lockoutwindow) {
700 $count = 0;
701 }
702
703 $count = $count+1;
704
705 set_user_preference('login_failed_count', $count, $user);
706 set_user_preference('login_failed_last', time(), $user);
707
708 if ($count >= $CFG->lockoutthreshold) {
709 login_lock_account($user);
710 }
711}
712
713/**
714 * Lockout user and send notification email.
715 *
716 * @param stdClass $user
717 */
718function login_lock_account($user) {
c484af5a 719 global $CFG;
b28247fe
PS
720
721 if ($user->mnethostid != $CFG->mnet_localhost_id) {
722 return;
723 }
724 if (isguestuser($user)) {
725 return;
726 }
727
728 if (get_user_preferences('login_lockout_ignored', 0, $user)) {
729 // This user can not be locked out.
730 return;
731 }
732
733 $alreadylockedout = get_user_preferences('login_lockout', 0, $user);
734
735 set_user_preference('login_lockout', time(), $user);
736
737 if ($alreadylockedout == 0) {
738 $secret = random_string(15);
739 set_user_preference('login_lockout_secret', $secret, $user);
740
c484af5a 741 $oldforcelang = force_current_language($user->lang);
b28247fe
PS
742
743 $site = get_site();
2b503e40 744 $supportuser = core_user::get_support_user();
b28247fe
PS
745
746 $data = new stdClass();
747 $data->firstname = $user->firstname;
748 $data->lastname = $user->lastname;
749 $data->username = $user->username;
750 $data->sitename = format_string($site->fullname);
751 $data->link = $CFG->wwwroot.'/login/unlock_account.php?u='.$user->id.'&s='.$secret;
752 $data->admin = generate_email_signoff();
753
754 $message = get_string('lockoutemailbody', 'admin', $data);
755 $subject = get_string('lockoutemailsubject', 'admin', format_string($site->fullname));
756
757 if ($message) {
758 // Directly email rather than using the messaging system to ensure its not routed to a popup or jabber.
759 email_to_user($user, $supportuser, $subject, $message);
760 }
761
c484af5a 762 force_current_language($oldforcelang);
b28247fe
PS
763 }
764}
765
766/**
767 * Unlock user account and reset timers.
768 *
769 * @param stdClass $user
770 */
771function login_unlock_account($user) {
772 unset_user_preference('login_lockout', $user);
773 unset_user_preference('login_failed_count', $user);
774 unset_user_preference('login_failed_last', $user);
775
776 // Note: do not clear the lockout secret because user might click on the link repeatedly.
777}