2007-09-14 05:12:21 +02:00
|
|
|
<?php
|
2016-06-15 06:03:16 +02:00
|
|
|
|
2017-04-22 06:30:10 +02:00
|
|
|
namespace SilverStripe\Security\MemberAuthenticator;
|
2016-06-23 01:37:22 +02:00
|
|
|
|
2016-08-19 00:51:35 +02:00
|
|
|
use SilverStripe\Control\Controller;
|
|
|
|
use SilverStripe\Control\Session;
|
|
|
|
use SilverStripe\Forms\Form;
|
2016-06-15 06:03:16 +02:00
|
|
|
use SilverStripe\ORM\ValidationResult;
|
2016-06-23 01:37:22 +02:00
|
|
|
use InvalidArgumentException;
|
2017-04-22 06:30:10 +02:00
|
|
|
use SilverStripe\Security\Authenticator as BaseAuthenticator;
|
|
|
|
use SilverStripe\Security\Security;
|
|
|
|
use SilverStripe\Security\Member;
|
2016-06-23 01:37:22 +02:00
|
|
|
|
2007-09-14 05:12:21 +02:00
|
|
|
/**
|
|
|
|
* Authenticator for the default "member" method
|
|
|
|
*
|
|
|
|
* @author Markus Lanthaler <markus@silverstripe.com>
|
|
|
|
*/
|
2017-04-22 06:30:10 +02:00
|
|
|
class Authenticator implements BaseAuthenticator
|
2016-11-29 00:31:16 +01:00
|
|
|
{
|
|
|
|
|
2017-04-22 06:30:10 +02:00
|
|
|
public function supportedServices()
|
|
|
|
{
|
|
|
|
// Bitwise-OR of all the supported services, to make a bitmask
|
|
|
|
return BaseAuthenticator::LOGIN | BaseAuthenticator::LOGOUT | BaseAuthenticator::CHANGE_PASSWORD
|
|
|
|
| BaseAuthenticator::RESET_PASSWORD | BaseAuthenticator::CMS_LOGIN;
|
|
|
|
}
|
|
|
|
|
2016-11-23 06:09:10 +01:00
|
|
|
/**
|
2017-04-22 06:30:10 +02:00
|
|
|
* @inherit
|
2016-11-23 06:09:10 +01:00
|
|
|
*/
|
2017-04-22 06:30:10 +02:00
|
|
|
public function authenticate($data, &$message)
|
|
|
|
{
|
|
|
|
$success = null;
|
|
|
|
|
|
|
|
// Find authenticated member
|
|
|
|
$member = $this->authenticateMember($data, $message, $success);
|
|
|
|
|
|
|
|
// Optionally record every login attempt as a {@link LoginAttempt} object
|
|
|
|
$this->recordLoginAttempt($data, $member, $success);
|
|
|
|
|
|
|
|
if ($member) {
|
|
|
|
Session::clear('BackURL');
|
|
|
|
}
|
|
|
|
|
|
|
|
return $success ? $member : null;
|
|
|
|
}
|
2016-11-23 06:09:10 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Attempt to find and authenticate member if possible from the given data
|
|
|
|
*
|
|
|
|
* @param array $data
|
|
|
|
* @param Form $form
|
|
|
|
* @param bool &$success Success flag
|
|
|
|
* @return Member Found member, regardless of successful login
|
|
|
|
*/
|
2017-04-22 06:30:10 +02:00
|
|
|
protected function authenticateMember($data, &$message, &$success)
|
2016-11-29 00:31:16 +01:00
|
|
|
{
|
2016-11-23 06:09:10 +01:00
|
|
|
// Default success to false
|
|
|
|
$success = false;
|
|
|
|
|
|
|
|
// Attempt to identify by temporary ID
|
|
|
|
$member = null;
|
|
|
|
$email = null;
|
|
|
|
if (!empty($data['tempid'])) {
|
|
|
|
// Find user by tempid, in case they are re-validating an existing session
|
|
|
|
$member = Member::member_from_tempid($data['tempid']);
|
2016-11-29 00:31:16 +01:00
|
|
|
if ($member) {
|
|
|
|
$email = $member->Email;
|
2016-11-23 06:09:10 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, get email from posted value instead
|
|
|
|
/** @skipUpgrade */
|
|
|
|
if (!$member && !empty($data['Email'])) {
|
|
|
|
$email = $data['Email'];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check default login (see Security::setDefaultAdmin())
|
|
|
|
$asDefaultAdmin = $email === Security::default_admin_username();
|
|
|
|
if ($asDefaultAdmin) {
|
|
|
|
// If logging is as default admin, ensure record is setup correctly
|
|
|
|
$member = Member::default_admin();
|
|
|
|
$success = !$member->isLockedOut() && Security::check_default_admin($email, $data['Password']);
|
|
|
|
//protect against failed login
|
|
|
|
if ($success) {
|
|
|
|
return $member;
|
|
|
|
}
|
2016-11-29 00:31:16 +01:00
|
|
|
}
|
|
|
|
|
2016-11-23 06:09:10 +01:00
|
|
|
// Attempt to identify user by email
|
|
|
|
if (!$member && $email) {
|
|
|
|
// Find user by email
|
|
|
|
$member = Member::get()
|
|
|
|
->filter(Member::config()->unique_identifier_field, $email)
|
|
|
|
->first();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Validate against member if possible
|
|
|
|
if ($member && !$asDefaultAdmin) {
|
|
|
|
$result = $member->checkPassword($data['Password']);
|
|
|
|
$success = $result->isValid();
|
|
|
|
} else {
|
2017-01-18 04:58:48 +01:00
|
|
|
$result = ValidationResult::create()->addError(_t(
|
2017-04-20 03:15:24 +02:00
|
|
|
'SilverStripe\\Security\\Member.ERRORWRONGCRED',
|
2017-01-18 04:58:48 +01:00
|
|
|
'The provided details don\'t seem to be correct. Please try again.'
|
|
|
|
));
|
2016-11-23 06:09:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Emit failure to member and form (if available)
|
|
|
|
if (!$success) {
|
|
|
|
if ($member) {
|
|
|
|
$member->registerFailedLogin();
|
|
|
|
}
|
2017-04-22 06:30:10 +02:00
|
|
|
$message = implode("; ", array_map(
|
|
|
|
function ($message) {
|
|
|
|
return $message['message'];
|
|
|
|
},
|
|
|
|
$result->getMessages()
|
|
|
|
));
|
2016-11-23 06:09:10 +01:00
|
|
|
} else {
|
2016-11-29 00:31:16 +01:00
|
|
|
if ($member) {
|
|
|
|
$member->registerSuccessfulLogin();
|
|
|
|
}
|
2016-11-23 06:09:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return $member;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Log login attempt
|
|
|
|
* TODO We could handle this with an extension
|
|
|
|
*
|
|
|
|
* @param array $data
|
|
|
|
* @param Member $member
|
|
|
|
*/
|
2017-04-22 06:30:10 +02:00
|
|
|
protected function recordLoginAttempt($data, $member)
|
2016-11-29 00:31:16 +01:00
|
|
|
{
|
|
|
|
if (!Security::config()->login_recording) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-11-23 06:09:10 +01:00
|
|
|
// Check email is valid
|
|
|
|
/** @skipUpgrade */
|
|
|
|
$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
|
|
|
|
Member::singleton()->extend('authenticationFailedUnknownUser', $data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$attempt->Email = $email;
|
|
|
|
$attempt->IP = Controller::curr()->getRequest()->getIP();
|
|
|
|
$attempt->write();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2017-04-22 06:30:10 +02:00
|
|
|
* @inherit
|
2016-11-23 06:09:10 +01:00
|
|
|
*/
|
2017-04-22 06:30:10 +02:00
|
|
|
public function getLostPasswordHandler($link)
|
2016-11-29 00:31:16 +01:00
|
|
|
{
|
2017-04-22 06:30:10 +02:00
|
|
|
return LostPasswordHandler::create($link, $this);
|
2016-11-23 06:09:10 +01:00
|
|
|
}
|
2016-11-29 00:31:16 +01:00
|
|
|
|
2016-11-23 06:09:10 +01:00
|
|
|
/**
|
2017-04-22 06:30:10 +02:00
|
|
|
* @inherit
|
2016-11-23 06:09:10 +01:00
|
|
|
*/
|
2017-04-22 06:30:10 +02:00
|
|
|
public function getChangePasswordHandler($link)
|
2016-11-29 00:31:16 +01:00
|
|
|
{
|
2017-04-22 06:30:10 +02:00
|
|
|
return ChangePasswordHandler::create($link, $this);
|
2016-11-23 06:09:10 +01:00
|
|
|
}
|
2016-11-29 00:31:16 +01:00
|
|
|
|
2017-04-22 06:30:10 +02:00
|
|
|
/**
|
|
|
|
* @inherit
|
|
|
|
*/
|
|
|
|
public function getLoginHandler($link)
|
2016-11-29 00:31:16 +01:00
|
|
|
{
|
2017-04-22 06:30:10 +02:00
|
|
|
return LoginHandler::create($link, $this);
|
2016-11-23 06:09:10 +01:00
|
|
|
}
|
2016-11-29 00:31:16 +01:00
|
|
|
|
2017-04-22 06:30:10 +02:00
|
|
|
public function getCMSLoginHandler($link)
|
2016-11-29 00:31:16 +01:00
|
|
|
{
|
2017-04-22 06:30:10 +02:00
|
|
|
return CMSMemberLoginHandler::create($controller, self::class, "LoginForm");
|
2016-11-23 06:09:10 +01:00
|
|
|
}
|
2007-09-14 05:12:21 +02:00
|
|
|
}
|