/**
  * Method to authenticate an user
  *
  * @param array $RAW_data Raw data to authenticate the user
  * @param Form $form Optional: If passed, better error messages can be
  *                             produced by using
  *                             {@link Form::sessionMessage()}
  * @return bool|Member Returns FALSE if authentication fails, otherwise
  *                     the member object
  * @see Security::setDefaultAdmin()
  */
 public static function authenticate($RAW_data, Form $form = null)
 {
     if (array_key_exists('Email', $RAW_data) && $RAW_data['Email']) {
         $SQL_user = Convert::raw2sql($RAW_data['Email']);
     } else {
         return false;
     }
     $isLockedOut = false;
     $result = null;
     // Default login (see Security::setDefaultAdmin())
     if (Security::check_default_admin($RAW_data['Email'], $RAW_data['Password'])) {
         $member = Security::findAnAdministrator();
     } else {
         $member = DataObject::get_one("Member", "\"" . Member::get_unique_identifier_field() . "\" = '{$SQL_user}' AND \"Password\" IS NOT NULL");
         if ($member) {
             $result = $member->checkPassword($RAW_data['Password']);
         } else {
             $result = new ValidationResult(false, _t('Member.ERRORWRONGCRED'));
         }
         if ($member && !$result->valid()) {
             $member->registerFailedLogin();
             $member = false;
         }
     }
     // Optionally record every login attempt as a {@link LoginAttempt} object
     /**
      * TODO We could handle this with an extension
      */
     if (Security::login_recording()) {
         $attempt = new LoginAttempt();
         if ($member) {
             // successful login (member is existing with matching password)
             $attempt->MemberID = $member->ID;
             $attempt->Status = 'Success';
             // Audit logging hook
             $member->extend('authenticated');
         } else {
             // failed login - we're trying to see if a user exists with this email (disregarding wrong passwords)
             $existingMember = DataObject::get_one("Member", "\"" . Member::get_unique_identifier_field() . "\" = '{$SQL_user}'");
             if ($existingMember) {
                 $attempt->MemberID = $existingMember->ID;
                 // Audit logging hook
                 $existingMember->extend('authenticationFailed');
             } else {
                 // Audit logging hook
                 singleton('Member')->extend('authenticationFailedUnknownUser', $RAW_data);
             }
             $attempt->Status = 'Failure';
         }
         if (is_array($RAW_data['Email'])) {
             user_error("Bad email passed to MemberAuthenticator::authenticate(): {$RAW_data['Email']}", E_USER_WARNING);
             return false;
         }
         $attempt->Email = $RAW_data['Email'];
         $attempt->IP = Controller::curr()->getRequest()->getIP();
         $attempt->write();
     }
     // Legacy migration to precision-safe password hashes.
     // A login-event with cleartext passwords is the only time
     // when we can rehash passwords to a different hashing algorithm,
     // bulk-migration doesn't work due to the nature of hashing.
     // See PasswordEncryptor_LegacyPHPHash class.
     if ($member && self::$migrate_legacy_hashes && array_key_exists($member->PasswordEncryption, self::$migrate_legacy_hashes)) {
         $member->Password = $RAW_data['Password'];
         $member->PasswordEncryption = self::$migrate_legacy_hashes[$member->PasswordEncryption];
         $member->write();
     }
     if ($member) {
         Session::clear('BackURL');
     } else {
         if ($form && $result) {
             $form->sessionMessage($result->message(), 'bad');
         }
     }
     return $member;
 }
 /**
  * Writes a message to the audit log
  *
  * @param object  $member       The member if found in the database
  * @param string  $anchor       The login name if the user
  * @param string  $action_type  What was tried?
  * @param string  $because      Reason for success
  * @param boolean $success      Did we succeed
  * @param string  $source_id    For which source
  **/
 public static function AuditLog($member, $anchor, $action_type, $because, $success, $source_id)
 {
     if (self::getAuditLogSStripe()) {
         //Use built-in mechanism
         $attempt = new LoginAttempt();
         if ($member) {
             $attempt->MemberID = $member->ID;
         } else {
             $attempt->MemberID = 0;
         }
         if ($success) {
             $attempt->Status = 'Success';
         } else {
             $attempt->Status = 'Failure';
         }
         $attempt->IP = Controller::curr()->getRequest()->getIP();
         $attempt->Email = $anchor . '@' . $source_id;
         $attempt->write();
     }
     if (!is_bool(self::getAuditLogFile())) {
         $logmessage = date(DATE_RFC822) . ' - ';
         if ($success) {
             $logmessage .= '[SUCCESS] ';
         } else {
             $logmessage .= '[FAILURE] ';
         }
         $logmessage .= 'action ' . $action_type . ' for user ' . $anchor . ' at ' . Controller::curr()->getRequest()->getIP() . ' from source ' . $source_id;
         if (!is_null($because)) {
             $logmessage .= ' because ' . $because;
         }
         if (!@error_log($logmessage . "\n", 3, self::getAuditLogFile())) {
             trigger_error('Unable to write logon attempt to ' . self::getAuditLogFile(), E_USER_ERROR);
         }
     }
 }
 /**
  * Log login attempt
  * TODO We could handle this with an extension
  *
  * @param array $data
  * @param Member $member
  * @param bool $success
  */
 protected static function record_login_attempt($data, $member, $success)
 {
     if (!Security::config()->login_recording) {
         return;
     }
     // Check email is valid
     $email = isset($data['Email']) ? $data['Email'] : null;
     if (is_array($email)) {
         throw new InvalidArgumentException("Bad email passed to MemberAuthenticator::authenticate(): {$email}");
     }
     $attempt = new LoginAttempt();
     if ($success) {
         // successful login (member is existing with matching password)
         $attempt->MemberID = $member->ID;
         $attempt->Status = 'Success';
         // Audit logging hook
         $member->extend('authenticated');
     } else {
         // Failed login - we're trying to see if a user exists with this email (disregarding wrong passwords)
         $attempt->Status = 'Failure';
         if ($member) {
             // Audit logging hook
             $attempt->MemberID = $member->ID;
             $member->extend('authenticationFailed');
         } else {
             // Audit logging hook
             singleton('Member')->extend('authenticationFailedUnknownUser', $data);
         }
     }
     $attempt->Email = $email;
     $attempt->IP = Controller::curr()->getRequest()->getIP();
     $attempt->write();
 }
 /**
  * Method to authenticate an user
  *
  * @param array $RAW_data Raw data to authenticate the user
  * @param Form $form Optional: If passed, better error messages can be
  *                             produced by using
  *                             {@link Form::sessionMessage()}
  * @return bool|Member Returns FALSE if authentication fails, otherwise
  *                     the member object
  * @see Security::setDefaultAdmin()
  */
 public static function authenticate($RAW_data, Form $form = null)
 {
     $SQL_user = Convert::raw2sql($RAW_data['Email']);
     $isLockedOut = false;
     // Default login (see Security::setDefaultAdmin())
     if (Security::check_default_admin($RAW_data['Email'], $RAW_data['Password'])) {
         $member = Security::findAnAdministrator();
     } else {
         $member = DataObject::get_one("Member", "Email = '{$SQL_user}' AND Password IS NOT NULL");
         if ($member && $member->checkPassword($RAW_data['Password']) == false) {
             if ($member->isLockedOut()) {
                 $isLockedOut = true;
             }
             $member->registerFailedLogin();
             $member = null;
         }
     }
     // Optionally record every login attempt as a {@link LoginAttempt} object
     /**
      * TODO We could handle this with an extension
      */
     if (Security::login_recording()) {
         $attempt = new LoginAttempt();
         if ($member) {
             // successful login (member is existing with matching password)
             $attempt->MemberID = $member->ID;
             $attempt->Status = 'Success';
             // Audit logging hook
             $member->extend('authenticated');
         } else {
             // failed login - we're trying to see if a user exists with this email (disregarding wrong passwords)
             $existingMember = DataObject::get_one("Member", "Email = '{$SQL_user}'");
             if ($existingMember) {
                 $attempt->MemberID = $existingMember->ID;
                 // Audit logging hook
                 $existingMember->extend('authenticationFailed');
             } else {
                 // Audit logging hook
                 singleton('Member')->extend('authenticationFailedUnknownUser', $RAW_data);
             }
             $attempt->Status = 'Failure';
         }
         if (is_array($RAW_data['Email'])) {
             user_error("Bad email passed to MemberAuthenticator::authenticate(): {$RAW_data['Email']}", E_USER_WARNING);
             return false;
         }
         $attempt->Email = $RAW_data['Email'];
         $attempt->IP = Controller::curr()->getRequest()->getIP();
         $attempt->write();
     }
     if ($member) {
         Session::clear("BackURL");
     } else {
         if ($isLockedOut) {
             if ($form) {
                 $form->sessionMessage(_t('Member.ERRORLOCKEDOUT', "Your account has been temporarily disabled because of too many failed attempts at logging in. Please try again in 20 minutes."), "bad");
             }
         } else {
             if ($form) {
                 $form->sessionMessage(_t('Member.ERRORWRONGCRED', "That doesn't seem to be the right e-mail address or password. Please try again."), "bad");
             }
         }
     }
     return $member;
 }