Beispiel #1
0
 /**
  * login
  *
  * The script checks provided name and password against the local database.
  *
  * If no record matches, and if the provided name explicitly mentions some origin server
  * (e.g., '*****@*****.**'), then this server is asked to authenticate the user.
  * This is done by transmitting the user name and the password to the origin server,
  * through a XML-RPC call ([code]drupal.login[/code] at [script]services/xml_rpc.php[/script]).
  * On success the origin server will provide the original id for the user profile.
  * Else a null id will be returned.
  *
  * On successful remote authentication the surfer will be considered as logged, either
  * as an associate, a member (default case), or as a subscriber (for closed communities).
  *
  * On successful remote authentication a 'shadow' user profile will be created locally,
  * using another id, and a copy of the authentication url saved in the password field.
  * Also the user description explicitly references the original user profile.
  * This local record may be referenced in pages published locally.
  *
  * This means that on subsequent visits the 'shadow' profile will be retrieved, and the origin
  * server will be sollicitated again for credentials validation.
  * As a consequence the validity of login data is always checked by the server that actually
  * stores the original user profile.
  * If the user profile is modified or is deleted this change will be taken into account on next login.
  *
  * @link http://drupal.org/node/312 Using distributed authentication (drupal.org)
  *
  * This script also allows for a last resort password.
  * When a webmaster has lost his password, and if there is no other associate to help,
  * he can modify manually the file [code]parameters/control.include.php[/code] to add
  * a parameter [code]$context['last_resort_password'][/code], followed by a long passphrase
  * of at least seven characters. For example:
  * [php]
  * $context['last_resort_password'] = '******';
  * [/php]
  *
  * Then he can authenticate normally, using this password, and any name.
  *
  * On successful login the returned array contains following named atributes:
  * - id - record in the table of users (can be a shadow record)
  * - nick_name - name to be displayed  in user menu, and server messages
  * - email - user e-mail address, if any
  * - capability - either 'A'ssociate, 'M'ember, 'S'ubscriber or '?'
  *
  * @param string the nickname or the email address of the user
  * @param string the submitted password
  * @return the record of the authenticated surfer, or NULL
  *
  * @see users/login.php
  * @see services/blog.php
  */
 public static function login($name, $password)
 {
     global $context;
     // using the last resort password
     if (isset($context['last_resort_password']) && strlen(trim($context['last_resort_password'])) >= 1 && $password == $context['last_resort_password']) {
         // this is an event to remember
         Logger::remember('users/users.php: ' . i18n::c('lrp has logged in'), i18n::c('Login using the last resort password'));
         // a fake associate
         $user = array();
         $user['id'] = 1;
         $user['nick_name'] = 'lrp';
         $user['email'] = '';
         $user['capability'] = 'A';
         return $user;
     }
     // user has not been authenticated yet
     $authenticated = FALSE;
     $item = NULL;
     // search a user profile locally
     $query = "SELECT * FROM " . SQL::table_name('users') . " AS users" . " WHERE users.email LIKE '" . SQL::escape($name) . "' OR users.nick_name LIKE '" . SQL::escape($name) . "' OR users.full_name LIKE '" . SQL::escape($name) . "'";
     if (isset($context['users_connection']) && ($item = SQL::query_first($query, FALSE, $context['users_connection']))) {
         // the user has been explicitly locked
         if ($item['capability'] == '?') {
             return NULL;
         } elseif ($item['authenticate_failures'] >= 3 && $item['authenticate_date'] > gmstrftime('%Y-%m-%d %H:%M:%S', time() - 3600)) {
             Logger::error(i18n::s('Wait for one hour to recover from too many failed authentications.'));
             return NULL;
             // successful local check
         } elseif (md5($password) == $item['password']) {
             $authenticated = TRUE;
         }
     }
     // we have to authenticate externally, if this has been explicitly allowed
     if (!$authenticated && isset($context['users_authenticator']) && $context['users_authenticator']) {
         // load and configure an authenticator instance
         include_once $context['path_to_root'] . 'users/authenticator.php';
         if (!($authenticator = Authenticator::bind($context['users_authenticator']))) {
             return NULL;
         }
         // submit full name to authenticator
         if (isset($item['full_name']) && trim($item['full_name']) && $authenticator->login($item['full_name'], $password)) {
             $authenticated = TRUE;
         } elseif ($authenticator->login($name, $password)) {
             $authenticated = TRUE;
         }
     }
     // we have to create a shadow record
     if ($authenticated && !isset($item['id'])) {
         // shadow record
         $fields = array();
         $fields['nick_name'] = $name;
         $fields['description'] = i18n::s('Authenticated externally.');
         $fields['password'] = '******';
         $fields['with_newsletters'] = 'Y';
         $fields['without_alerts'] = 'N';
         $fields['without_confirmations'] = 'N';
         $fields['authenticate_date'] = gmstrftime('%Y-%m-%d %H:%M:%S');
         $fields['authenticate_failures'] = 0;
         // stop on error
         if (!($fields['id'] = Users::post($fields))) {
             return NULL;
         }
         // retrieve the shadow record
         $item = Users::get($fields['id']);
     }
     // bad credentials
     if (!$authenticated && isset($item['id'])) {
         // increment failing authentications during last hour
         if (isset($item['authenticate_date']) && $item['authenticate_date'] >= gmstrftime('%Y-%m-%d %H:%M:%S', time() - 3600)) {
             $query = "UPDATE " . SQL::table_name('users') . " SET authenticate_failures=authenticate_failures+1" . " WHERE id = " . $item['id'];
             if ($item['authenticate_failures'] >= 2) {
                 Logger::error(i18n::s('Wait for one hour to recover from too many failed authentications.'));
             } elseif ($item['authenticate_failures'] == 1) {
                 Logger::error(i18n::s('You have 1 grace authentication attempt.'));
             }
             // first failure in a row
         } else {
             $query = "UPDATE " . SQL::table_name('users') . " SET authenticate_date = '" . gmstrftime('%Y-%m-%d %H:%M:%S') . "'" . ", authenticate_failures=1" . " WHERE id = " . $item['id'];
             Logger::error(i18n::s('You have 2 grace authentication attempts.'));
         }
         // update target record
         SQL::query($query, FALSE, $context['users_connection']);
         // no user record is returned
         return NULL;
     }
     // not authenticated, or no record
     if (!$authenticated || !isset($item['id'])) {
         return NULL;
     }
     // generate a random handle if necessary
     $handle = '';
     if (!isset($item['handle']) || !$item['handle']) {
         $item['handle'] = md5(rand());
         $handle = ", handle='" . $item['handle'] . "' ";
     }
     // remember silently the date of the last login, and reset authentication counter
     $query = "UPDATE " . SQL::table_name('users') . " SET login_date='" . gmstrftime('%Y-%m-%d %H:%M:%S') . "'" . ", login_address='" . $_SERVER['REMOTE_ADDR'] . "'" . ", authenticate_date='" . gmstrftime('%Y-%m-%d %H:%M:%S') . "'" . ", authenticate_failures=0" . $handle . " WHERE id = " . $item['id'];
     SQL::query($query, FALSE, $context['users_connection']);
     // valid user - date of previous login is transmitted as well
     return $item;
 }