Commit 8bab6674 authored by alexpott's avatar alexpott

Issue #2206687 by damiankloip: Replace user_authenticate with a UserAuth service.

parent 8e47a4eb
services:
authentication.basic_auth:
class: Drupal\basic_auth\Authentication\Provider\BasicAuth
arguments: ['@config.factory']
arguments: ['@config.factory', '@user.auth']
tags:
- { name: authentication_provider, priority: 100 }
......@@ -11,6 +11,7 @@
use Drupal\Core\Authentication\AuthenticationProviderInterface;
use Drupal\Core\Config\Config;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\user\UserAuthInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
......@@ -28,14 +29,22 @@ class BasicAuth implements AuthenticationProviderInterface {
*/
protected $configFactory;
/**
* The user auth service.
*
* @var \Drupal\user\UserAuthInterface
*/
protected $userAuth;
/**
* Constructs a HTTP basic authentication provider object.
*
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The config factory.
*/
public function __construct(ConfigFactoryInterface $config_factory) {
public function __construct(ConfigFactoryInterface $config_factory, UserAuthInterface $user_auth) {
$this->configFactory = $config_factory;
$this->userAuth = $user_auth;
}
/**
......@@ -53,7 +62,7 @@ public function applies(Request $request) {
public function authenticate(Request $request) {
$username = $request->headers->get('PHP_AUTH_USER');
$password = $request->headers->get('PHP_AUTH_PW');
$uid = user_authenticate($username, $password);
$uid = $this->userAuth->authenticate($username, $password);
if ($uid) {
return user_load($uid);
}
......
......@@ -9,6 +9,7 @@
use Drupal\Core\Flood\FloodInterface;
use Drupal\Core\Form\FormBase;
use Drupal\user\UserAuthInterface;
use Drupal\user\UserStorageControllerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
......@@ -31,6 +32,13 @@ class UserLoginForm extends FormBase {
*/
protected $userStorage;
/**
* The user authentication object.
*
* @var \Drupal\user\UserAuthInterface
*/
protected $userAuth;
/**
* Constructs a new UserLoginForm.
*
......@@ -38,10 +46,13 @@ class UserLoginForm extends FormBase {
* The flood service.
* @param \Drupal\user\UserStorageControllerInterface $user_storage
* The user storage controller.
* @param \Drupal\user\UserAuthInterface $user_auth
* The user authentication object.
*/
public function __construct(FloodInterface $flood, UserStorageControllerInterface $user_storage) {
public function __construct(FloodInterface $flood, UserStorageControllerInterface $user_storage, UserAuthInterface $user_auth) {
$this->flood = $flood;
$this->userStorage = $user_storage;
$this->userAuth = $user_auth;
}
/**
......@@ -50,7 +61,8 @@ public function __construct(FloodInterface $flood, UserStorageControllerInterfac
public static function create(ContainerInterface $container) {
return new static(
$container->get('flood'),
$container->get('entity.manager')->getStorageController('user')
$container->get('entity.manager')->getStorageController('user'),
$container->get('user.auth')
);
}
......@@ -165,7 +177,7 @@ public function validateAuthentication(array &$form, array &$form_state) {
}
// We are not limited by flood control, so try to authenticate.
// Set $form_state['uid'] as a flag for self::validateFinal().
$form_state['uid'] = user_authenticate($form_state['values']['name'], $password);
$form_state['uid'] = $this->userAuth->authenticate($form_state['values']['name'], $password);
}
}
......
<?php
/**
* @file
* Contains \Drupal\user\UserAuth.
*/
namespace Drupal\user;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\EntityStorageControllerInterface;
use Drupal\Core\Password\PasswordInterface;
/**
* Validates user authentication credentials.
*/
class UserAuth implements UserAuthInterface {
/**
* The user storage.
*
* @var \Drupal\Core\Entity\EntityStorageControllerInterface
*/
protected $storage;
/**
* The password service.
*
* @var \Drupal\Core\Password\PasswordInterface
*/
protected $passwordChecker;
/**
* Constructs a UserAuth object.
*
* @param \Drupal\Core\Entity\EntityStorageControllerInterface $storage
* The user storage.
* @param \Drupal\Core\Password\PasswordInterface $password_checker
* The password service.
*/
public function __construct(EntityManagerInterface $entity_manager, PasswordInterface $password_checker) {
$this->storage = $entity_manager->getStorageController('user');
$this->passwordChecker = $password_checker;
}
/**
* {@inheritdoc}
*/
public function authenticate($username, $password) {
$uid = FALSE;
if (!empty($username) && !empty($password)) {
$account_search = $this->storage->loadByProperties(array('name' => $username));
if ($account = reset($account_search)) {
if ($this->passwordChecker->check($password, $account)) {
// Successful authentication.
$uid = $account->id();
// Update user to new password scheme if needed.
if ($this->passwordChecker->userNeedsNewHash($account)) {
$account->setPassword($password);
$account->save();
}
}
}
}
return $uid;
}
}
<?php
/**
* @file
* Contains \Drupal\user\UserAuthInterface.
*/
namespace Drupal\user;
/**
* An interface for validating user authentication credentials.
*/
interface UserAuthInterface {
/**
* Validates user authentication credentials.
*
* @param string $username
* The user name to authenticate.
* @param string $password
* A plain-text password, such as trimmed text from form values.
* @return int|bool
* The user's uid on success, or FALSE on failure to authenticate.
*/
public function authenticate($username, $password);
}
<?php
/**
* @file
* Contains \Drupal\user\Tests\UserAuthTest.
*/
namespace Drupal\user\Tests;
use Drupal\Tests\UnitTestCase;
use Drupal\user\UserAuth;
/**
* Tests the UserAuth class.
*
* @group Drupal
* @group User
*
* @coverDefaultClass \Drupal\user\UserAuth
*/
class UserAuthTest extends UnitTestCase {
/**
* The mock user storage controller.
*
* @var \Drupal\Core\Entity\EntityStorageControllerInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $userStorage;
/**
* The mocked password service.
*
* @var \Drupal\Core\Password\PasswordInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $passwordService;
/**
* The mock user.
*
* @var \Drupal\user\Entity\User|\PHPUnit_Framework_MockObject_MockObject
*/
protected $testUser;
/**
* The user auth object under test.
*
* @var \Drupal\user\UserAuth
*/
protected $userAuth;
/**
* The test username.
*
* @var string
*/
protected $username = 'test_user';
/**
* The test password
*
* @var string
*/
protected $password = 'password';
/**
* {@inheritdoc}
*/
public static function getInfo() {
return array(
'name' => 'User auth service',
'description' => 'Tests the user auth service',
'group' => 'User',
);
}
/**
* {@inheritdoc}
*/
public function setUp() {
$this->userStorage = $this->getMock('Drupal\Core\Entity\EntityStorageControllerInterface');
$entity_manager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
// Getting the user storage controller should only happen once per test.
$entity_manager->expects($this->once())
->method('getStorageController')
->with('user')
->will($this->returnValue($this->userStorage));
$this->passwordService = $this->getMock('Drupal\Core\Password\PasswordInterface');
$this->testUser = $this->getMockBuilder('Drupal\user\Entity\User')
->disableOriginalConstructor()
->setMethods(array('id', 'setPassword', 'save'))
->getMock();
$this->userAuth = new UserAuth($entity_manager, $this->passwordService);
}
/**
* Tests failing authentication with missing credential parameters.
*
* @covers ::authenticate()
*
* @dataProvider providerTestAuthenticateWithMissingCredentials
*/
public function testAuthenticateWithMissingCredentials($username, $password) {
$this->userStorage->expects($this->never())
->method('loadByProperties');
$this->assertFalse($this->userAuth->authenticate($username, $password));
}
/**
* Data provider for testAuthenticateWithMissingCredentials().
*
* @return array
*/
public function providerTestAuthenticateWithMissingCredentials() {
return array(
array(NULL, NULL),
array(NULL, ''),
array('', NULL),
array('', ''),
);
}
/**
* Tests the authenticate method with no account returned.
*
* @covers ::authenticate()
*/
public function testAuthenticateWithNoAccountReturned() {
$this->userStorage->expects($this->once())
->method('loadByProperties')
->with(array('name' => $this->username))
->will($this->returnValue(array()));
$this->assertFalse($this->userAuth->authenticate($this->username, $this->password));
}
/**
* Tests the authenticate method with an incorrect password.
*
* @covers ::authenticate()
*/
public function testAuthenticateWithIncorrectPassword() {
$this->userStorage->expects($this->once())
->method('loadByProperties')
->with(array('name' => $this->username))
->will($this->returnValue(array($this->testUser)));
$this->passwordService->expects($this->once())
->method('check')
->with($this->password, $this->testUser)
->will($this->returnValue(FALSE));
$this->assertFalse($this->userAuth->authenticate($this->username, $this->password));
}
/**
* Tests the authenticate method with a correct password.
*
* @covers ::authenticate()
*/
public function testAuthenticateWithCorrectPassword() {
$this->testUser->expects($this->once())
->method('id')
->will($this->returnValue(1));
$this->userStorage->expects($this->once())
->method('loadByProperties')
->with(array('name' => $this->username))
->will($this->returnValue(array($this->testUser)));
$this->passwordService->expects($this->once())
->method('check')
->with($this->password, $this->testUser)
->will($this->returnValue(TRUE));
$this->assertsame(1, $this->userAuth->authenticate($this->username, $this->password));
}
/**
* Tests the authenticate method with a correct password and new password hash.
*
* @covers ::authenticate()
*/
public function testAuthenticateWithCorrectPasswordAndNewPasswordHash() {
$this->testUser->expects($this->once())
->method('id')
->will($this->returnValue(1));
$this->testUser->expects($this->once())
->method('setPassword')
->with($this->password);
$this->testUser->expects($this->once())
->method('save');
$this->userStorage->expects($this->once())
->method('loadByProperties')
->with(array('name' => $this->username))
->will($this->returnValue(array($this->testUser)));
$this->passwordService->expects($this->once())
->method('check')
->with($this->password, $this->testUser)
->will($this->returnValue(TRUE));
$this->passwordService->expects($this->once())
->method('userNeedsNewHash')
->with($this->testUser)
->will($this->returnValue(TRUE));
$this->assertsame(1, $this->userAuth->authenticate($this->username, $this->password));
}
}
......@@ -796,26 +796,12 @@ function user_admin_paths() {
* A plain-text password, such as trimmed text from form values.
* @return
* The user's uid on success, or FALSE on failure to authenticate.
*
* @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
* Use \Drupal\user\UserAuth::authenticate() instead.
*/
function user_authenticate($name, $password) {
$uid = FALSE;
if (!empty($name) && !empty($password)) {
$account = user_load_by_name($name);
if ($account) {
$password_hasher = \Drupal::service('password');
if ($password_hasher->check($password, $account)) {
// Successful authentication.
$uid = $account->id();
// Update user to new password scheme if needed.
if ($password_hasher->userNeedsNewHash($account)) {
$account->setPassword($password);
$account->save();
}
}
}
}
return $uid;
return \Drupal::service('user.auth')->authenticate($name, $password);
}
/**
......
......@@ -33,3 +33,6 @@ services:
user.permissions_hash:
class: Drupal\user\PermissionsHash
arguments: ['@private_key', '@cache.cache']
user.auth:
class: Drupal\user\UserAuth
arguments: ['@entity.manager', '@password']
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment