Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.
/login/ -> index.php (source)

Differences Between: [Versions 310 and 403] [Versions 311 and 403] [Versions 39 and 403] [Versions 400 and 403] [Versions 401 and 403] [Versions 402 and 403]

   1  <?php
   2  
   3  // This file is part of Moodle - http://moodle.org/
   4  //
   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.
  14  //
  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  
  18  /**
  19   * Main login page.
  20   *
  21   * @package    core
  22   * @subpackage auth
  23   * @copyright  1999 onwards Martin Dougiamas  http://dougiamas.com
  24   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  25   */
  26  
  27  require('../config.php');
  28  require_once ('lib.php');
  29  
  30  redirect_if_major_upgrade_required();
  31  
  32  $testsession = optional_param('testsession', 0, PARAM_INT); // test session works properly
  33  $anchor      = optional_param('anchor', '', PARAM_RAW);     // Used to restore hash anchor to wantsurl.
  34  
  35  $resendconfirmemail = optional_param('resendconfirmemail', false, PARAM_BOOL);
  36  
  37  // It might be safe to do this for non-Behat sites, or there might
  38  // be a security risk. For now we only allow it on Behat sites.
  39  // If you wants to do the analysis, you may be able to remove the
  40  // if (BEHAT_SITE_RUNNING).
  41  if (defined('BEHAT_SITE_RUNNING') && BEHAT_SITE_RUNNING) {
  42      $wantsurl    = optional_param('wantsurl', '', PARAM_LOCALURL);   // Overrides $SESSION->wantsurl if given.
  43      if ($wantsurl !== '') {
  44          $SESSION->wantsurl = (new moodle_url($wantsurl))->out(false);
  45      }
  46  }
  47  
  48  $context = context_system::instance();
  49  $PAGE->set_url("$CFG->wwwroot/login/index.php");
  50  $PAGE->set_context($context);
  51  $PAGE->set_pagelayout('login');
  52  
  53  /// Initialize variables
  54  $errormsg = '';
  55  $infomsg = '';
  56  $errorcode = 0;
  57  
  58  // login page requested session test
  59  if ($testsession) {
  60      if ($testsession == $USER->id) {
  61          if (isset($SESSION->wantsurl)) {
  62              $urltogo = $SESSION->wantsurl;
  63          } else {
  64              $urltogo = $CFG->wwwroot.'/';
  65          }
  66          unset($SESSION->wantsurl);
  67          redirect($urltogo);
  68      } else {
  69          // TODO: try to find out what is the exact reason why sessions do not work
  70          $errormsg = get_string("cookiesnotenabled");
  71          $errorcode = 1;
  72      }
  73  }
  74  
  75  /// Check for timed out sessions
  76  if (!empty($SESSION->has_timed_out)) {
  77      $session_has_timed_out = true;
  78      unset($SESSION->has_timed_out);
  79  } else {
  80      $session_has_timed_out = false;
  81  }
  82  
  83  $frm  = false;
  84  $user = false;
  85  
  86  $authsequence = get_enabled_auth_plugins(); // Auths, in sequence.
  87  foreach($authsequence as $authname) {
  88      $authplugin = get_auth_plugin($authname);
  89      // The auth plugin's loginpage_hook() can eventually set $frm and/or $user.
  90      $authplugin->loginpage_hook();
  91  }
  92  
  93  
  94  /// Define variables used in page
  95  $site = get_site();
  96  
  97  // Ignore any active pages in the navigation/settings.
  98  // We do this because there won't be an active page there, and by ignoring the active pages the
  99  // navigation and settings won't be initialised unless something else needs them.
 100  $PAGE->navbar->ignore_active();
 101  $loginsite = get_string("loginsite");
 102  $PAGE->navbar->add($loginsite);
 103  
 104  if ($user !== false or $frm !== false or $errormsg !== '') {
 105      // some auth plugin already supplied full user, fake form data or prevented user login with error message
 106  
 107  } else if (!empty($SESSION->wantsurl) && file_exists($CFG->dirroot.'/login/weblinkauth.php')) {
 108      // Handles the case of another Moodle site linking into a page on this site
 109      //TODO: move weblink into own auth plugin
 110      include($CFG->dirroot.'/login/weblinkauth.php');
 111      if (function_exists('weblink_auth')) {
 112          $user = weblink_auth($SESSION->wantsurl);
 113      }
 114      if ($user) {
 115          $frm->username = $user->username;
 116      } else {
 117          $frm = data_submitted();
 118      }
 119  
 120  } else {
 121      $frm = data_submitted();
 122  }
 123  
 124  // Restore the #anchor to the original wantsurl. Note that this
 125  // will only work for internal auth plugins, SSO plugins such as
 126  // SAML / CAS / OIDC will have to handle this correctly directly.
 127  if ($anchor && isset($SESSION->wantsurl) && strpos($SESSION->wantsurl, '#') === false) {
 128      $wantsurl = new moodle_url($SESSION->wantsurl);
 129      $wantsurl->set_anchor(substr($anchor, 1));
 130      $SESSION->wantsurl = $wantsurl->out();
 131  }
 132  
 133  /// Check if the user has actually submitted login data to us
 134  
 135  if ($frm and isset($frm->username)) {                             // Login WITH cookies
 136  
 137      $frm->username = trim(core_text::strtolower($frm->username));
 138  
 139      if (is_enabled_auth('none') ) {
 140          if ($frm->username !== core_user::clean_field($frm->username, 'username')) {
 141              $errormsg = get_string('username').': '.get_string("invalidusername");
 142              $errorcode = 2;
 143              $user = null;
 144          }
 145      }
 146  
 147      if ($user) {
 148          // The auth plugin has already provided the user via the loginpage_hook() called above.
 149      } else if (($frm->username == 'guest') and empty($CFG->guestloginbutton)) {
 150          $user = false;    /// Can't log in as guest if guest button is disabled
 151          $frm = false;
 152      } else {
 153          if (empty($errormsg)) {
 154              $logintoken = isset($frm->logintoken) ? $frm->logintoken : '';
 155              $loginrecaptcha = $frm->{'g-recaptcha-response'} ?? false;
 156              $user = authenticate_user_login($frm->username, $frm->password, false, $errorcode, $logintoken, $loginrecaptcha);
 157          }
 158      }
 159  
 160      // Intercept 'restored' users to provide them with info & reset password
 161      if (!$user and $frm and is_restored_user($frm->username)) {
 162          $PAGE->set_title(get_string('restoredaccount'));
 163          $PAGE->set_heading($site->fullname);
 164          echo $OUTPUT->header();
 165          echo $OUTPUT->heading(get_string('restoredaccount'));
 166          echo $OUTPUT->box(get_string('restoredaccountinfo'), 'generalbox boxaligncenter');
 167          require_once ('restored_password_form.php'); // Use our "supplanter" login_forgot_password_form. MDL-20846
 168          $form = new login_forgot_password_form('forgot_password.php', array('username' => $frm->username));
 169          $form->display();
 170          echo $OUTPUT->footer();
 171          die;
 172      }
 173  
 174      if ($user) {
 175  
 176          // language setup
 177          if (isguestuser($user)) {
 178              // no predefined language for guests - use existing session or default site lang
 179              unset($user->lang);
 180  
 181          } else if (!empty($user->lang)) {
 182              // unset previous session language - use user preference instead
 183              unset($SESSION->lang);
 184          }
 185  
 186          if (empty($user->confirmed)) {       // This account was never confirmed
 187              $PAGE->set_title(get_string("mustconfirm"));
 188              $PAGE->set_heading($site->fullname);
 189              echo $OUTPUT->header();
 190              echo $OUTPUT->heading(get_string("mustconfirm"));
 191              if ($resendconfirmemail) {
 192                  if (!send_confirmation_email($user)) {
 193                      echo $OUTPUT->notification(get_string('emailconfirmsentfailure'), \core\output\notification::NOTIFY_ERROR);
 194                  } else {
 195                      echo $OUTPUT->notification(get_string('emailconfirmsentsuccess'), \core\output\notification::NOTIFY_SUCCESS);
 196                  }
 197              }
 198              echo $OUTPUT->box(get_string("emailconfirmsent", "", s($user->email)), "generalbox boxaligncenter");
 199              $resendconfirmurl = new moodle_url('/login/index.php',
 200                  [
 201                      'username' => $frm->username,
 202                      'password' => $frm->password,
 203                      'resendconfirmemail' => true,
 204                      'logintoken' => \core\session\manager::get_login_token()
 205                  ]
 206              );
 207              echo $OUTPUT->single_button($resendconfirmurl, get_string('emailconfirmationresend'));
 208              echo $OUTPUT->footer();
 209              die;
 210          }
 211  
 212      /// Let's get them all set up.
 213          complete_user_login($user);
 214  
 215          \core\session\manager::apply_concurrent_login_limit($user->id, session_id());
 216  
 217          // sets the username cookie
 218          if (!empty($CFG->nolastloggedin)) {
 219              // do not store last logged in user in cookie
 220              // auth plugins can temporarily override this from loginpage_hook()
 221              // do not save $CFG->nolastloggedin in database!
 222  
 223          } else if (empty($CFG->rememberusername)) {
 224              // no permanent cookies, delete old one if exists
 225              set_moodle_cookie('');
 226  
 227          } else {
 228              set_moodle_cookie($USER->username);
 229          }
 230  
 231          $urltogo = core_login_get_return_url();
 232  
 233      /// check if user password has expired
 234      /// Currently supported only for ldap-authentication module
 235          $userauth = get_auth_plugin($USER->auth);
 236          if (!isguestuser() and !empty($userauth->config->expiration) and $userauth->config->expiration == 1) {
 237              $externalchangepassword = false;
 238              if ($userauth->can_change_password()) {
 239                  $passwordchangeurl = $userauth->change_password_url();
 240                  if (!$passwordchangeurl) {
 241                      $passwordchangeurl = $CFG->wwwroot.'/login/change_password.php';
 242                  } else {
 243                      $externalchangepassword = true;
 244                  }
 245              } else {
 246                  $passwordchangeurl = $CFG->wwwroot.'/login/change_password.php';
 247              }
 248              $days2expire = $userauth->password_expire($USER->username);
 249              $PAGE->set_title($loginsite);
 250              $PAGE->set_heading("$site->fullname");
 251              if (intval($days2expire) > 0 && intval($days2expire) < intval($userauth->config->expiration_warning)) {
 252                  echo $OUTPUT->header();
 253                  echo $OUTPUT->confirm(get_string('auth_passwordwillexpire', 'auth', $days2expire), $passwordchangeurl, $urltogo);
 254                  echo $OUTPUT->footer();
 255                  exit;
 256              } elseif (intval($days2expire) < 0 ) {
 257                  if ($externalchangepassword) {
 258                      // We end the session if the change password form is external. This prevents access to the site
 259                      // until the password is correctly changed.
 260                      require_logout();
 261                  } else {
 262                      // If we use the standard change password form, this user preference will be reset when the password
 263                      // is changed. Until then it will prevent access to the site.
 264                      set_user_preference('auth_forcepasswordchange', 1, $USER);
 265                  }
 266                  echo $OUTPUT->header();
 267                  echo $OUTPUT->confirm(get_string('auth_passwordisexpired', 'auth'), $passwordchangeurl, $urltogo);
 268                  echo $OUTPUT->footer();
 269                  exit;
 270              }
 271          }
 272  
 273          // Discard any errors before the last redirect.
 274          unset($SESSION->loginerrormsg);
 275          unset($SESSION->logininfomsg);
 276  
 277          // test the session actually works by redirecting to self
 278          $SESSION->wantsurl = $urltogo;
 279          redirect(new moodle_url(get_login_url(), array('testsession'=>$USER->id)));
 280  
 281      } else {
 282          if (empty($errormsg)) {
 283              if ($errorcode == AUTH_LOGIN_UNAUTHORISED) {
 284                  $errormsg = get_string("unauthorisedlogin", "", $frm->username);
 285              } else if ($errorcode == AUTH_LOGIN_FAILED_RECAPTCHA) {
 286                  $errormsg = get_string('missingrecaptchachallengefield');
 287              } else {
 288                  $errormsg = get_string("invalidlogin");
 289                  $errorcode = 3;
 290              }
 291          }
 292      }
 293  }
 294  
 295  /// Detect problems with timedout sessions
 296  if ($session_has_timed_out and !data_submitted()) {
 297      $errormsg = get_string('sessionerroruser', 'error');
 298      $errorcode = 4;
 299  }
 300  
 301  /// First, let's remember where the user was trying to get to before they got here
 302  
 303  if (empty($SESSION->wantsurl)) {
 304      $SESSION->wantsurl = null;
 305      $referer = get_local_referer(false);
 306      if ($referer &&
 307              $referer != $CFG->wwwroot &&
 308              $referer != $CFG->wwwroot . '/' &&
 309              $referer != $CFG->wwwroot . '/login/' &&
 310              strpos($referer, $CFG->wwwroot . '/login/?') !== 0 &&
 311              strpos($referer, $CFG->wwwroot . '/login/index.php') !== 0) { // There might be some extra params such as ?lang=.
 312          $SESSION->wantsurl = $referer;
 313      }
 314  }
 315  
 316  /// Redirect to alternative login URL if needed
 317  if (!empty($CFG->alternateloginurl)) {
 318      $loginurl = new moodle_url($CFG->alternateloginurl);
 319  
 320      $loginurlstr = $loginurl->out(false);
 321  
 322      if ($SESSION->wantsurl != '' && strpos($SESSION->wantsurl, $loginurlstr) === 0) {
 323          // We do not want to return to alternate url.
 324          $SESSION->wantsurl = null;
 325      }
 326  
 327      // If error code then add that to url.
 328      if ($errorcode) {
 329          $loginurl->param('errorcode', $errorcode);
 330      }
 331  
 332      redirect($loginurl->out(false));
 333  }
 334  
 335  /// Generate the login page with forms
 336  
 337  if (!isset($frm) or !is_object($frm)) {
 338      $frm = new stdClass();
 339  }
 340  
 341  if (empty($frm->username) && $authsequence[0] != 'shibboleth') {  // See bug 5184
 342      if (!empty($_GET["username"])) {
 343          // we do not want data from _POST here
 344          $frm->username = clean_param($_GET["username"], PARAM_RAW); // we do not want data from _POST here
 345      } else {
 346          $frm->username = get_moodle_cookie();
 347      }
 348  
 349      $frm->password = "";
 350  }
 351  
 352  if (!empty($SESSION->loginerrormsg) || !empty($SESSION->logininfomsg)) {
 353      // We had some messages before redirect, show them now.
 354      $errormsg = $SESSION->loginerrormsg ?? '';
 355      $infomsg = $SESSION->logininfomsg ?? '';
 356      unset($SESSION->loginerrormsg);
 357      unset($SESSION->logininfomsg);
 358  
 359  } else if ($testsession) {
 360      // No need to redirect here.
 361      unset($SESSION->loginerrormsg);
 362      unset($SESSION->logininfomsg);
 363  
 364  } else if ($errormsg or !empty($frm->password)) {
 365      // We must redirect after every password submission.
 366      if ($errormsg) {
 367          $SESSION->loginerrormsg = $errormsg;
 368      }
 369      redirect(new moodle_url('/login/index.php'));
 370  }
 371  
 372  $PAGE->set_title($loginsite);
 373  $PAGE->set_heading("$site->fullname");
 374  
 375  echo $OUTPUT->header();
 376  
 377  if (isloggedin() and !isguestuser()) {
 378      // prevent logging when already logged in, we do not want them to relogin by accident because sesskey would be changed
 379      echo $OUTPUT->box_start();
 380      $logout = new single_button(new moodle_url('/login/logout.php', array('sesskey'=>sesskey(),'loginpage'=>1)), get_string('logout'), 'post');
 381      $continue = new single_button(new moodle_url('/'), get_string('cancel'), 'get');
 382      echo $OUTPUT->confirm(get_string('alreadyloggedin', 'error', fullname($USER)), $logout, $continue);
 383      echo $OUTPUT->box_end();
 384  } else {
 385      $loginform = new \core_auth\output\login($authsequence, $frm->username);
 386      $loginform->set_error($errormsg);
 387      $loginform->set_info($infomsg);
 388      echo $OUTPUT->render($loginform);
 389  }
 390  
 391  echo $OUTPUT->footer();