Splitting the getting of the user and checking credentials into two steps

This looks like a subjective change (one more method, but the method implementations are
simpler), but it wasn't. The problem was that the UserChecker checkPreAuth should happen
*after* we get the user, but *before* the credentials are checked, and that wasn't possible
before this change. Now it is.
This commit is contained in:
Ryan Weaver 2015-05-18 09:14:21 -04:00
parent 6edb9e1b06
commit ffdbc66534
3 changed files with 41 additions and 12 deletions

View File

@ -27,7 +27,7 @@ interface GuardAuthenticatorInterface extends AuthenticationEntryPointInterface
* as any type (e.g. an associate array). If you return null, authentication
* will be skipped.
*
* Whatever value you return here will be passed to authenticate()
* Whatever value you return here will be passed to getUser() and checkCredentials()
*
* For example, for a form login, you might:
*
@ -47,19 +47,33 @@ interface GuardAuthenticatorInterface extends AuthenticationEntryPointInterface
public function getCredentials(Request $request);
/**
* Return a UserInterface object based on the credentials OR throw
* an AuthenticationException.
* Return a UserInterface object based on the credentials
*
* The *credentials* are the return value from getCredentials()
*
* You may throw an AuthenticationException if you wish. If you return
* null, then a UsernameNotFoundException is thrown for you.
*
* @param mixed $credentials
* @param UserProviderInterface $userProvider
*
* @throws AuthenticationException
*
* @return UserInterface
* @return UserInterface|null
*/
public function authenticate($credentials, UserProviderInterface $userProvider);
public function getUser($credentials, UserProviderInterface $userProvider);
/**
* Throw an AuthenticationException if the credentials are invalid
*
* The *credentials* are the return value from getCredentials()
*
* @param mixed $credentials
* @param UserInterface $user
* @throws AuthenticationException
* @return void
*/
public function checkCredentials($credentials, UserInterface $user);
/**
* Create an authenticated token for the given user.

View File

@ -4,6 +4,7 @@ namespace Symfony\Component\Security\Guard\Provider;
use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface;
use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Guard\GuardAuthenticatorInterface;
use Symfony\Component\Security\Guard\Token\GuardTokenInterface;
use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken;
@ -95,18 +96,28 @@ class GuardAuthenticationProvider implements AuthenticationProviderInterface
private function authenticateViaGuard(GuardAuthenticatorInterface $guardAuthenticator, PreAuthenticationGuardToken $token)
{
// get the user from the GuardAuthenticator
$user = $guardAuthenticator->authenticate($token->getCredentials(), $this->userProvider);
$user = $guardAuthenticator->getUser($token->getCredentials(), $this->userProvider);
if (null === $user) {
throw new UsernameNotFoundException(sprintf(
'Null returned from %s::getUser()',
get_class($guardAuthenticator)
));
}
if (!$user instanceof UserInterface) {
throw new \UnexpectedValueException(sprintf(
'The %s::authenticate method must return a UserInterface. You returned %s.',
'The %s::getUser method must return a UserInterface. You returned %s.',
get_class($guardAuthenticator),
is_object($user) ? get_class($user) : gettype($user)
));
}
// check the AdvancedUserInterface methods!
// check the preAuth UserChecker
$this->userChecker->checkPreAuth($user);
// check the credentials
$guardAuthenticator->checkCredentials($token->getCredentials(), $user);
// check the postAuth UserChecker
$this->userChecker->checkPostAuth($user);
// turn the UserInterface into a TokenInterface

View File

@ -43,21 +43,25 @@ class GuardAuthenticationProviderTest extends \PHPUnit_Framework_TestCase
'username' => '_weaverryan_test_user',
'password' => 'guard_auth_ftw',
);
$this->preAuthenticationToken->expects($this->once())
$this->preAuthenticationToken->expects($this->atLeastOnce())
->method('getCredentials')
->will($this->returnValue($enteredCredentials));
// authenticators A and C are never called
$authenticatorA->expects($this->never())
->method('authenticate');
->method('getUser');
$authenticatorC->expects($this->never())
->method('authenticate');
->method('getUser');
$mockedUser = $this->getMock('Symfony\Component\Security\Core\User\UserInterface');
$authenticatorB->expects($this->once())
->method('authenticate')
->method('getUser')
->with($enteredCredentials, $this->userProvider)
->will($this->returnValue($mockedUser));
// checkCredentials is called
$authenticatorB->expects($this->once())
->method('checkCredentials')
->with($enteredCredentials, $mockedUser);
$authedToken = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
$authenticatorB->expects($this->once())
->method('createAuthenticatedToken')