Exemple #1
0
 /**
  * Provides a hook into the login page.
  *
  * @param object &$frm Form object.
  * @param object &$user User object.
  */
 public function loginpage_hook(&$frm, &$user)
 {
     global $DB;
     if (empty($frm)) {
         $frm = data_submitted();
     }
     if (empty($frm)) {
         return true;
     }
     $autoappend = get_config('auth_oidc', 'autoappend');
     if (empty($autoappend)) {
         // If we're not doing autoappend, just let things flow naturally.
         return true;
     }
     $username = $frm->username;
     $password = $frm->password;
     $auth = 'oidc';
     $existinguser = $DB->get_record('user', ['username' => $username]);
     if (!empty($existinguser)) {
         // We don't want to prevent access to existing accounts.
         return true;
     }
     $username .= $autoappend;
     $success = $this->user_login($username, $password);
     if ($success !== true) {
         // No o365 user, continue normally.
         return false;
     }
     $existinguser = $DB->get_record('user', ['username' => $username]);
     if (!empty($existinguser)) {
         $user = $existinguser;
         return true;
     }
     // The user is authenticated but user creation may be disabled.
     if (!empty($CFG->authpreventaccountcreation)) {
         $failurereason = AUTH_LOGIN_UNAUTHORISED;
         // Trigger login failed event.
         $event = \core\event\user_login_failed::create(array('other' => array('username' => $username, 'reason' => $failurereason)));
         $event->trigger();
         error_log('[client ' . getremoteaddr() . "]  {$CFG->wwwroot}  Unknown user, can not create new accounts:  {$username}  " . $_SERVER['HTTP_USER_AGENT']);
         return false;
     }
     $user = create_user_record($username, $password, $auth);
     return true;
 }
 /**
  * Handle a login event.
  *
  * @param string $oidcuniqid A unique identifier for the user.
  * @param array $authparams Parameters receieved from the auth request.
  * @param array $tokenparams Parameters received from the token request.
  * @param \auth_oidc\jwt $idtoken A JWT object representing the received id_token.
  */
 protected function handlelogin($oidcuniqid, $authparams, $tokenparams, $idtoken)
 {
     global $DB, $CFG;
     $tokenrec = $DB->get_record('auth_oidc_token', ['oidcuniqid' => $oidcuniqid]);
     if (!empty($tokenrec)) {
         $username = $tokenrec->username;
         $this->updatetoken($tokenrec->id, $authparams, $tokenparams);
     } else {
         // Use 'upn' if available for username (Azure-specific), or fall back to lower-case oidcuniqid.
         $username = $idtoken->claim('upn');
         if (empty($username)) {
             $username = strtolower($oidcuniqid);
         }
         $matchedwith = $this->check_for_matched($username);
         if (!empty($matchedwith)) {
             $matchedwith->aadupn = $username;
             throw new \moodle_exception('errorusermatched', 'local_o365', null, $matchedwith);
         }
         $tokenrec = $this->createtoken($oidcuniqid, $username, $authparams, $tokenparams, $idtoken);
     }
     $existinguserparams = ['username' => $username, 'mnethostid' => $CFG->mnet_localhost_id];
     if ($DB->record_exists('user', $existinguserparams) !== true) {
         // User does not exist. Create user if site allows, otherwise fail.
         if (empty($CFG->authpreventaccountcreation)) {
             $user = create_user_record($username, null, 'oidc');
         } else {
             // Trigger login failed event.
             $failurereason = AUTH_LOGIN_NOUSER;
             $eventdata = ['other' => ['username' => $username, 'reason' => $failurereason]];
             $event = \core\event\user_login_failed::create($eventdata);
             $event->trigger();
             throw new \moodle_exception('errorauthloginfailednouser', 'auth_oidc');
         }
     }
     $user = authenticate_user_login($username, null, true);
     if (empty($user)) {
         throw new \moodle_exception('errorauthloginfailednouser', 'auth_oidc');
     }
     complete_user_login($user);
     return true;
 }
Exemple #3
0
/**
 * Authenticates a user against the chosen authentication mechanism
 *
 * Given a username and password, this function looks them
 * up using the currently selected authentication mechanism,
 * and if the authentication is successful, it returns a
 * valid $user object from the 'user' table.
 *
 * Uses auth_ functions from the currently active auth module
 *
 * After authenticate_user_login() returns success, you will need to
 * log that the user has logged in, and call complete_user_login() to set
 * the session up.
 *
 * Note: this function works only with non-mnet accounts!
 *
 * @param string $username  User's username (or also email if $CFG->authloginviaemail enabled)
 * @param string $password  User's password
 * @param bool $ignorelockout useful when guessing is prevented by other mechanism such as captcha or SSO
 * @param int $failurereason login failure reason, can be used in renderers (it may disclose if account exists)
 * @return stdClass|false A {@link $USER} object or false if error
 */
function authenticate_user_login($username, $password, $ignorelockout = false, &$failurereason = null)
{
    global $CFG, $DB;
    require_once "{$CFG->libdir}/authlib.php";
    if ($user = get_complete_user_data('username', $username, $CFG->mnet_localhost_id)) {
        // we have found the user
    } else {
        if (!empty($CFG->authloginviaemail)) {
            if ($email = clean_param($username, PARAM_EMAIL)) {
                $select = "mnethostid = :mnethostid AND LOWER(email) = LOWER(:email) AND deleted = 0";
                $params = array('mnethostid' => $CFG->mnet_localhost_id, 'email' => $email);
                $users = $DB->get_records_select('user', $select, $params, 'id', 'id', 0, 2);
                if (count($users) === 1) {
                    // Use email for login only if unique.
                    $user = reset($users);
                    $user = get_complete_user_data('id', $user->id);
                    $username = $user->username;
                }
                unset($users);
            }
        }
    }
    $authsenabled = get_enabled_auth_plugins();
    if ($user) {
        // Use manual if auth not set.
        $auth = empty($user->auth) ? 'manual' : $user->auth;
        if (in_array($user->auth, $authsenabled)) {
            $authplugin = get_auth_plugin($user->auth);
            $authplugin->pre_user_login_hook($user);
        }
        if (!empty($user->suspended)) {
            $failurereason = AUTH_LOGIN_SUSPENDED;
            // Trigger login failed event.
            $event = \core\event\user_login_failed::create(array('userid' => $user->id, 'other' => array('username' => $username, 'reason' => $failurereason)));
            $event->trigger();
            error_log('[client ' . getremoteaddr() . "]  {$CFG->wwwroot}  Suspended Login:  {$username}  " . $_SERVER['HTTP_USER_AGENT']);
            return false;
        }
        if ($auth == 'nologin' or !is_enabled_auth($auth)) {
            // Legacy way to suspend user.
            $failurereason = AUTH_LOGIN_SUSPENDED;
            // Trigger login failed event.
            $event = \core\event\user_login_failed::create(array('userid' => $user->id, 'other' => array('username' => $username, 'reason' => $failurereason)));
            $event->trigger();
            error_log('[client ' . getremoteaddr() . "]  {$CFG->wwwroot}  Disabled Login:  {$username}  " . $_SERVER['HTTP_USER_AGENT']);
            return false;
        }
        $auths = array($auth);
    } else {
        // Check if there's a deleted record (cheaply), this should not happen because we mangle usernames in delete_user().
        if ($DB->get_field('user', 'id', array('username' => $username, 'mnethostid' => $CFG->mnet_localhost_id, 'deleted' => 1))) {
            $failurereason = AUTH_LOGIN_NOUSER;
            // Trigger login failed event.
            $event = \core\event\user_login_failed::create(array('other' => array('username' => $username, 'reason' => $failurereason)));
            $event->trigger();
            error_log('[client ' . getremoteaddr() . "]  {$CFG->wwwroot}  Deleted Login:  {$username}  " . $_SERVER['HTTP_USER_AGENT']);
            return false;
        }
        // User does not exist.
        $auths = $authsenabled;
        $user = new stdClass();
        $user->id = 0;
    }
    if ($ignorelockout) {
        // Some other mechanism protects against brute force password guessing, for example login form might include reCAPTCHA
        // or this function is called from a SSO script.
    } else {
        if ($user->id) {
            // Verify login lockout after other ways that may prevent user login.
            if (login_is_lockedout($user)) {
                $failurereason = AUTH_LOGIN_LOCKOUT;
                // Trigger login failed event.
                $event = \core\event\user_login_failed::create(array('userid' => $user->id, 'other' => array('username' => $username, 'reason' => $failurereason)));
                $event->trigger();
                error_log('[client ' . getremoteaddr() . "]  {$CFG->wwwroot}  Login lockout:  {$username}  " . $_SERVER['HTTP_USER_AGENT']);
                return false;
            }
        } else {
            // We can not lockout non-existing accounts.
        }
    }
    foreach ($auths as $auth) {
        $authplugin = get_auth_plugin($auth);
        // On auth fail fall through to the next plugin.
        if (!$authplugin->user_login($username, $password)) {
            continue;
        }
        // Successful authentication.
        if ($user->id) {
            // User already exists in database.
            if (empty($user->auth)) {
                // For some reason auth isn't set yet.
                $DB->set_field('user', 'auth', $auth, array('id' => $user->id));
                $user->auth = $auth;
            }
            // If the existing hash is using an out-of-date algorithm (or the legacy md5 algorithm), then we should update to
            // the current hash algorithm while we have access to the user's password.
            update_internal_user_password($user, $password);
            if ($authplugin->is_synchronised_with_external()) {
                // Update user record from external DB.
                $user = update_user_record_by_id($user->id);
            }
        } else {
            // The user is authenticated but user creation may be disabled.
            if (!empty($CFG->authpreventaccountcreation)) {
                $failurereason = AUTH_LOGIN_UNAUTHORISED;
                // Trigger login failed event.
                $event = \core\event\user_login_failed::create(array('other' => array('username' => $username, 'reason' => $failurereason)));
                $event->trigger();
                error_log('[client ' . getremoteaddr() . "]  {$CFG->wwwroot}  Unknown user, can not create new accounts:  {$username}  " . $_SERVER['HTTP_USER_AGENT']);
                return false;
            } else {
                $user = create_user_record($username, $password, $auth);
            }
        }
        $authplugin->sync_roles($user);
        foreach ($authsenabled as $hau) {
            $hauth = get_auth_plugin($hau);
            $hauth->user_authenticated_hook($user, $username, $password);
        }
        if (empty($user->id)) {
            $failurereason = AUTH_LOGIN_NOUSER;
            // Trigger login failed event.
            $event = \core\event\user_login_failed::create(array('other' => array('username' => $username, 'reason' => $failurereason)));
            $event->trigger();
            return false;
        }
        if (!empty($user->suspended)) {
            // Just in case some auth plugin suspended account.
            $failurereason = AUTH_LOGIN_SUSPENDED;
            // Trigger login failed event.
            $event = \core\event\user_login_failed::create(array('userid' => $user->id, 'other' => array('username' => $username, 'reason' => $failurereason)));
            $event->trigger();
            error_log('[client ' . getremoteaddr() . "]  {$CFG->wwwroot}  Suspended Login:  {$username}  " . $_SERVER['HTTP_USER_AGENT']);
            return false;
        }
        login_attempt_valid($user);
        $failurereason = AUTH_LOGIN_OK;
        return $user;
    }
    // Failed if all the plugins have failed.
    if (debugging('', DEBUG_ALL)) {
        error_log('[client ' . getremoteaddr() . "]  {$CFG->wwwroot}  Failed Login:  {$username}  " . $_SERVER['HTTP_USER_AGENT']);
    }
    if ($user->id) {
        login_attempt_failed($user);
        $failurereason = AUTH_LOGIN_FAILED;
        // Trigger login failed event.
        $event = \core\event\user_login_failed::create(array('userid' => $user->id, 'other' => array('username' => $username, 'reason' => $failurereason)));
        $event->trigger();
    } else {
        $failurereason = AUTH_LOGIN_NOUSER;
        // Trigger login failed event.
        $event = \core\event\user_login_failed::create(array('other' => array('username' => $username, 'reason' => $failurereason)));
        $event->trigger();
    }
    return false;
}