silverstripe-framework/security/MemberAuthenticator.php
Geoff Munn f4de365be8 API CHANGE: Unique_identifier now accepted as the login requirement, allowing alternatives to 'Email'
git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/branches/2.4@97270 467b73ca-7a2a-4603-9d3b-597d59a354a9
2011-02-02 14:18:10 +13:00

146 lines
4.6 KiB
PHP

<?php
/**
* Authenticator for the default "member" method
*
* @author Markus Lanthaler <markus@silverstripe.com>
* @package sapphire
* @subpackage security
*/
class MemberAuthenticator extends Authenticator {
/**
* @var Array Contains encryption algorithm identifiers.
* If set, will migrate to new precision-safe password hashing
* upon login. See http://open.silverstripe.org/ticket/3004.
*/
static $migrate_legacy_hashes = array(
'md5' => 'md5_v2.4',
'sha1' => 'sha1_v2.4'
);
/**
* 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",
"\"" . Member::get_unique_identifier_field() . "\" = '$SQL_user' AND \"Password\" IS NOT NULL"
);
if($member && ($member->checkPassword($RAW_data['Password']) == false)) {
if($member->isLockedOut()) $isLockedOut = true;
$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 // only migrate after successful login
&& 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($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;
}
/**
* Method that creates the login form for this authentication method
*
* @param Controller The parent controller, necessary to create the
* appropriate form action tag
* @return Form Returns the login form to use with this authentication
* method
*/
public static function get_login_form(Controller $controller) {
return Object::create("MemberLoginForm", $controller, "LoginForm");
}
/**
* Get the name of the authentication method
*
* @return string Returns the name of the authentication method.
*/
public static function get_name() {
return _t('MemberAuthenticator.TITLE', "E-mail &amp; Password");
}
}
?>