You are here

function _persistent_login_check in Persistent Login 7

Same name and namespace in other branches
  1. 5 persistent_login.module \_persistent_login_check()
  2. 6 persistent_login.module \_persistent_login_check()

Do the real work. Note that we may be in BOOTSTRAP_PAGE_CACHE mode with few modules loaded.

If a non-logged in user has a valid Persistent Login cookie, log her in, disable the old cookie, and issue a new one for next time. Then reload the current page so the user is logged in from the beginning.

If a non-logged in user has an invalid PL cookie that indicates an attack has occurred, panic.

If a user logged in by Persistent Login tries to access a protected page, redirect them to the login page. Their remembered login is preserved, though, so they can skip the login and keep browsing non-protected pages.

1 call to _persistent_login_check()
persistent_login_boot in ./persistent_login.module
Implements hook_boot(). Before a cached page is served, perform a Persistent Login if appropriate. Persistent Login must operate during boot because if page caching is enabled, other hooks are never invoked unless the user is already logged in.

File

./persistent_login.module, line 347
Provide a "Remember Me" checkbox in the login form.

Code

function _persistent_login_check() {
  global $user;
  $path = isset($_GET['q']) ? $_GET['q'] : '';

  // Do not interfere with login/logout pages. Note that we're performing this
  // check during hook_boot(), Drupal has not already normalized the path, so
  // we need to take care of the path prefix defined for language negotiation.
  $mode = variable_get('language_negotiation_language', array());
  if (isset($mode['locale-url'])) {
    $ll_enabled = language_list('enabled');
    foreach (array_keys($ll_enabled[1]) as $prefix) {
      if (!empty($prefix) && preg_match('`^(?:' . preg_quote($prefix) . '/){0,1}(?:user/login|user/logout)$`', $path)) {
        return;
      }
    }
  }
  elseif ($path == 'user/login' || $path == 'user/logout') {
    return;
  }
  $now = REQUEST_TIME;
  $cookie_name = _persistent_login_get_cookie_name();
  if ($user->uid == 0 && isset($_COOKIE[$cookie_name]) && !isset($_SESSION['persistent_login_check'])) {

    // For efficiency, only check once per session unless something changes.
    $_SESSION['persistent_login_check'] = TRUE;
    list($uid, $series, $token) = explode(':', $_COOKIE[$cookie_name]);

    // Determine if the token is valid by looking for it in the db.
    $res = db_query("SELECT u.name, pl.uid, pl.series as pl_series, pl.token as pl_token, pl.expires as pl_expires FROM {persistent_login} pl INNER JOIN {users} u USING (uid) WHERE u.status = :status AND pl.uid = :uid AND pl.series = :series", array(
      ':status' => 1,
      ':uid' => $uid,
      ':series' => $series,
    ));
    $r = $res
      ->fetchAssoc();
    if (!is_array($r) || count($r) == 0) {

      // $uid:$series is invalid
      return;
    }
    else {
      if ($r['pl_expires'] > 0 && $r['pl_expires'] < REQUEST_TIME) {

        // $uid:$series has expired
        return;
      }
    }

    // now, any outcome requires this
    require_once DRUPAL_ROOT . '/includes/common.inc';
    require_once DRUPAL_ROOT . '/' . variable_get('path_inc', 'includes/path.inc');
    require_once DRUPAL_ROOT . '/includes/theme.inc';
    if ($r['pl_token'] === $token) {

      // Delete the one-time use persistent login cookie.
      _persistent_login_invalidate('used', "uid = :uid AND series = :series", array(
        ':uid' => $uid,
        ':series' => $series,
      ));

      // The Persistent Login cookie is valid.  $r is a 'user form'
      // that contains only name, uid, pl_series, pl_token, and
      // pl_expires.  Add persistent_login so we and other modules can
      // tell what is going on.
      //
      $r['persistent_login'] = 1;

      // Log in the user.  Use user_external_login() so all the right
      // things happen.  Be sure to override persistent_login_login to
      // TRUE afterwards (our hook_user sets it to FALSE).
      //
      // user_external_login() requires user.module and
      // drupal_get_form() which needs system.module... just finish booting.
      drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
      $account = user_load($r['uid']);

      // Check if user is blocked.
      $state['values']['name'] = $account->name;
      user_login_name_validate(array(), $state);
      if (!form_get_errors()) {

        // Load global $user and perform final login tasks.
        $form_state['uid'] = $account->uid;
        $form_state['values'] = array();
        $form_state['values']['persistent_login'] = 1;
        user_login_submit(array(), $form_state);
      }
      else {
        return;
      }
      $_SESSION['persistent_login_login'] = TRUE;

      // Only welcome the user back once per session.
      if (empty($_SESSION['persistent_login_welcomed']) && variable_get('persistent_login_welcome', TRUE)) {
        drupal_set_message(t('Welcome back, %name.', array(
          '%name' => format_username($account),
        )));
      }
      $_SESSION['persistent_login_welcomed'] = TRUE;

      // Reload this page as the user.  If page caching is enabled,
      // the user was not logged in until now and so the page may have
      // come from the cache.  Also, some other init hook may care.
      // Also, note that we prevent redirections to front page path.
      if (empty($_POST)) {
        if (!isset($_GET['destination']) && drupal_is_front_page()) {
          drupal_goto('');
        }
        else {
          $dest = drupal_get_destination();
          $_GET['destination'] = $dest['destination'];
          drupal_goto();
        }
      }

      // Only reached if POST data available.
      return;
    }
    else {

      // The Persistent Login cookie is NOT valid, but $uid:$series
      // was right.  This means two browsers are sharing the cookie,
      // so someone is cheating.  Panic.
      // watchdog() needs a module that is not loaded yet during hook_boot(),
      // and t() needs the language initialized... just finish booting.
      drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);

      // Reset PL state in $_SESSION.
      $d = array();
      _persistent_login_invalidate('stolen', 'uid = :uid', array(
        ':uid' => $uid,
      ));
      persistent_login_user_logout($user);

      // Delete all open sessions for this user.  Use $uid from the
      // PL cookie, not $user->uid which is still 0.  No need to
      // regenerate the session, user will be anonymous on next visit.
      drupal_session_destroy_uid($uid);

      // Log the event, warn the user.
      watchdog('security', 'Stolen Persistent Login session for user %user detected.', array(
        '%user' => $r['name'],
      ), WATCHDOG_ERROR);
      drupal_set_message(t('<p><b>SECURITY ALERT!</b></p><p>You previously logged in to this site and checked the <em>Remember me</em> box.  At that time, this site stored a "login cookie" on your web browser that it uses to identify you each time you return.  However, the login cookie that your browser just provided is incorrect.  One possible cause of this error is that your web browser cookies have been stolen and used by someone else to impersonate you at this site.</p><p>As a precaution, we logged out all of your current sessions and deactivated all your remembered logins to this site.  You can log in again now.</p>'), 'error');
      drupal_goto();
      return;
    }
  }
}