Version: current

      1 <?php
      2 /**
      3  * Authenticator for the default "member" method
      4  *
      5  * @author Markus Lanthaler <markus@silverstripe.com>
      6  * @package sapphire
      7  * @subpackage security
      8  */
      9 class MemberAuthenticator extends Authenticator {
     10
     11     /**
     12      * @var Array Contains encryption algorithm identifiers.
     13      *  If set, will migrate to new precision-safe password hashing
     14      *  upon login. See http://open.silverstripe.org/ticket/3004.
     15      */
     16     static $migrate_legacy_hashes = array(
     17         'md5' => 'md5_v2.4',
     18         'sha1' => 'sha1_v2.4'
     19     );
     20
     21   /**
     22    * Method to authenticate an user
     23    *
     24    * @param array $RAW_data Raw data to authenticate the user
     25    * @param Form $form Optional: If passed, better error messages can be
     26    *                             produced by using
     27    *                             {@link Form::sessionMessage()}
     28    * @return bool|Member Returns FALSE if authentication fails, otherwise
     29    *                     the member object
     30    * @see Security::setDefaultAdmin()
     31    */
     32   public static function authenticate($RAW_data, Form $form = null) {
     33     $SQL_user = Convert::raw2sql($RAW_data['Email']);
     34     $isLockedOut = false;
     35     $result = null;
     36
     37     // Default login (see Security::setDefaultAdmin())
     38     if(Security::check_default_admin($RAW_data['Email'], $RAW_data['Password'])) {
     39         $member = Security::findAnAdministrator();
     40     } else {
     41         $member = DataObject::get_one(
     42             "Member",
     43             "\"" . Member::get_unique_identifier_field() . "\" = '$SQL_user' AND \"Password\" IS NOT NULL"
     44         );
     45
     46         if($member) {
     47             $result = $member->checkPassword($RAW_data['Password']);
     48         } else {
     49             $result = new ValidationResult(false, _t('Member.ERRORWRONGCRED'));
     50         }
     51
     52         if($member && !$result->valid()) {
     53             $member->registerFailedLogin();
     54             $member = false;
     55         }
     56     }
     57
     58     // Optionally record every login attempt as a {@link LoginAttempt} object
     59     /**
     60      * TODO We could handle this with an extension
     61      */
     62     if(Security::login_recording()) {
     63         $attempt = new LoginAttempt();
     64         if($member) {
     65             // successful login (member is existing with matching password)
     66             $attempt->MemberID = $member->ID;
     67             $attempt->Status = 'Success';
     68
     69             // Audit logging hook
     70             $member->extend('authenticated');
     71         } else {
     72             // failed login - we're trying to see if a user exists with this email (disregarding wrong passwords)
     73             $existingMember = DataObject::get_one("Member", "\"" . Member::get_unique_identifier_field() . "\" = '$SQL_user'");
     74             if($existingMember) {
     75                 $attempt->MemberID = $existingMember->ID;
     76
     77                 // Audit logging hook
     78                 $existingMember->extend('authenticationFailed');
     79             } else {
     80
     81                 // Audit logging hook
     82                 singleton('Member')->extend('authenticationFailedUnknownUser', $RAW_data);
     83             }
     84             $attempt->Status = 'Failure';
     85         }
     86         if(is_array($RAW_data['Email'])) {
     87             user_error("Bad email passed to MemberAuthenticator::authenticate(): $RAW_data[Email]", E_USER_WARNING);
     88             return false;
     89         }
     90
     91         $attempt->Email = $RAW_data['Email'];
     92         $attempt->IP = Controller::curr()->getRequest()->getIP();
     93         $attempt->write();
     94     }
     95
     96     // Legacy migration to precision-safe password hashes.
     97     // A login-event with cleartext passwords is the only time
     98     // when we can rehash passwords to a different hashing algorithm,
     99     // bulk-migration doesn't work due to the nature of hashing.
    100     // See PasswordEncryptor_LegacyPHPHash class.
    101     if(
    102         $member // only migrate after successful login
    103         && self::$migrate_legacy_hashes
    104         && array_key_exists($member->PasswordEncryption, self::$migrate_legacy_hashes)
    105     ) {
    106         $member->Password = $RAW_data['Password'];
    107         $member->PasswordEncryption = self::$migrate_legacy_hashes[$member->PasswordEncryption];
    108         $member->write();
    109     }
    110
    111         if($member) {
    112             Session::clear('BackURL');
    113         } else {
    114             if($form && $result) $form->sessionMessage($result->message(), 'bad');
    115         }
    116
    117         return $member;
    118     }
    119
    120
    121   /**
    122    * Method that creates the login form for this authentication method
    123    *
    124    * @param Controller The parent controller, necessary to create the
    125    *                   appropriate form action tag
    126    * @return Form Returns the login form to use with this authentication
    127    *              method
    128    */
    129   public static function get_login_form(Controller $controller) {
    130     return Object::create("MemberLoginForm", $controller, "LoginForm");
    131   }
    132
    133
    134   /**
    135    * Get the name of the authentication method
    136    *
    137    * @return string Returns the name of the authentication method.
    138    */
    139   public static function get_name() {
    140         return _t('MemberAuthenticator.TITLE', "E-mail &amp; Password");
    141     }
    142 }
    143
    144 ?>

[Raise a SilverStripe Framework issue/bug](https://github.com/silverstripe/silverstripe-framework/issues/new)
- [Raise a SilverStripe CMS issue/bug](https://github.com/silverstripe/silverstripe-cms/issues/new)
- Please use the Silverstripe Forums to ask development related questions. -