Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

New Guard Authentication System (e.g. putting the joy back into security) #14673

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 24 commits into from
Sep 24, 2015
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
330aa7f
Improving phpdoc on AuthenticationEntryPointInterface so people that …
weaverryan May 17, 2015
05af97c
Initial commit (but after some polished work) of the new Guard authen…
weaverryan May 17, 2015
a0bceb4
adding Guard tests
weaverryan May 17, 2015
873ed28
Renaming the tokens to be clear they are "post" and "pre" auth - also…
weaverryan May 17, 2015
180e2c7
Properly handles "post auth" tokens that have become not authenticated
weaverryan May 17, 2015
6c180c7
Adding an edge case - this should not happen anyways
weaverryan May 17, 2015
c73c32e
Thanks fabbot!
weaverryan May 17, 2015
eb158cb
Updating interface method per suggestion - makes sense to me, Request…
weaverryan May 18, 2015
d693721
Adding periods at the end of exceptions, and changing one class name …
weaverryan May 18, 2015
6edb9e1
Tweaking docblock on interface thanks to @iltar
weaverryan May 18, 2015
ffdbc66
Splitting the getting of the user and checking credentials into two s…
weaverryan May 18, 2015
7de05be
A few more changes thanks to @iltar
weaverryan May 18, 2015
7a94994
Thanks again fabbot!
weaverryan May 18, 2015
81432f9
Adding missing factory registration
weaverryan May 25, 2015
293c8a1
meaningless author and license changes
weaverryan Sep 20, 2015
0501761
Allowing for other authenticators to be checked
weaverryan Sep 20, 2015
31f9cae
Adding a base class to assist with form login authentication
weaverryan Sep 20, 2015
c9d9430
Adding logging on this step and switching the order - not for any hu…
weaverryan Sep 20, 2015
396a162
Tweaks thanks to Wouter
weaverryan Sep 20, 2015
302235e
Fixing a bug where having an authentication failure would log you out.
weaverryan Sep 21, 2015
dd485f4
Adding a new exception and throwing it when the User changes
weaverryan Sep 21, 2015
e353833
fabbot
weaverryan Sep 21, 2015
d763134
Removing unnecessary override
weaverryan Sep 22, 2015
a01ed35
Adding the necessary files so that Guard can be its own installable c…
weaverryan Sep 24, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Renaming the tokens to be clear they are "post" and "pre" auth - also…
… adding an interface

The reason is that the GuardAuthenticationProvider *must* respond to *all* tokens
created by the system - both "pre auth" and "post auth" tokens. The reason is that
if a "post auth" token becomes not authenticated (e.g. because the user changes between
requests), then it may be passed to the provider system. If no providers respond (which
was the case before this commit), then AuthenticationProviderManager throws an exception.

The next commit will properly handle these "post auth" + "no-longer-authenticated" tokens,
which should cause a log out.
  • Loading branch information
weaverryan committed Sep 20, 2015
commit 873ed284d26b937efe4ead5c7b5abce13fea9d09
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,26 @@
namespace Symfony\Component\Security\Guard;

use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Guard\Token\GenericGuardToken;
use Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken;

/**
* An optional base class that creates a GenericGuardToken for you
* An optional base class that creates a PostAuthenticationGuardToken for you
*
* @author Ryan Weaver <weaverryan@gmail.com>
*/
abstract class AbstractGuardAuthenticator implements GuardAuthenticatorInterface
{
/**
* Shortcut to create a GenericGuardToken for you, if you don't really
* Shortcut to create a PostAuthenticationGuardToken for you, if you don't really
* care about which authenticated token you're using
*
* @param UserInterface $user
* @param string $providerKey
* @return GenericGuardToken
* @return PostAuthenticationGuardToken
*/
public function createAuthenticatedToken(UserInterface $user, $providerKey)
{
return new GenericGuardToken(
return new PostAuthenticationGuardToken(
$user,
$providerKey,
$user->getRoles()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\Security\Guard\GuardAuthenticatorHandler;
use Symfony\Component\Security\Guard\Token\NonAuthenticatedGuardToken;
use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken;
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
use Symfony\Component\Security\Guard\GuardAuthenticatorInterface;
use Psr\Log\LoggerInterface;
Expand Down Expand Up @@ -86,7 +86,7 @@ private function executeGuardAuthenticator($uniqueGuardKey, GuardAuthenticatorIn
}

// create a token with the unique key, so that the provider knows which authenticator to use
$token = new NonAuthenticatedGuardToken($credentials, $uniqueGuardKey);
$token = new PreAuthenticationGuardToken($credentials, $uniqueGuardKey);

if (null !== $this->logger) {
$this->logger->info('Passing guard token information to the GuardAuthenticationProvider', array('firewall_key' => $this->providerKey, 'authenticator' => get_class($guardAuthenticator)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@
namespace Symfony\Component\Security\Guard\Provider;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I propose to move this namespace into Guard\Authentication, just as done in Security Core. Or move the class in Guard\Authentication directly (to avoid long FQCNs). The -AuthenticationProvider prefix already makes it clear it's an authentication provider, no need to duplicate that in the namespaces imo (in my ideal world, it would be Guard\Authentication\Provider\Guard).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Finally updating this PR :). I don't understand what you're proposing here - when you suggest Guard\Authentication... - do you actually mean that, or actually Symfony\Component\Security\Guard\Authentication...? I'm not convinced I have the classes organized in the most efficient way, and it's an easy change to still make.


use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface;
use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Guard\GuardAuthenticatorInterface;
use Symfony\Component\Security\Guard\Token\NonAuthenticatedGuardToken;
use Symfony\Component\Security\Guard\Token\GuardTokenInterface;
use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken;
use Symfony\Component\Security\Core\User\UserCheckerInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;

/**
* Responsible for accepting the NonAuthenticatedGuardToken and calling
* Responsible for accepting the PreAuthenticationGuardToken and calling
* the correct authenticator to retrieve the authenticated token
*
* @author Ryan Weaver <weaverryan@gmail.com>
Expand Down Expand Up @@ -43,12 +46,12 @@ public function __construct(array $guardAuthenticators, UserProviderInterface $u
/**
* Finds the correct authenticator for the token and calls it
*
* @param NonAuthenticatedGuardToken $token
* @param GuardTokenInterface $token
* @return TokenInterface
*/
public function authenticate(TokenInterface $token)
{
if (!$token instanceof NonAuthenticatedGuardToken) {
if (!$this->supports($token)) {
throw new \InvalidArgumentException('GuardAuthenticationProvider only supports NonAuthenticatedGuardToken');
}

Expand All @@ -69,7 +72,7 @@ public function authenticate(TokenInterface $token)
));
}

private function authenticateViaGuard(GuardAuthenticatorInterface $guardAuthenticator, NonAuthenticatedGuardToken $token)
private function authenticateViaGuard(GuardAuthenticatorInterface $guardAuthenticator, PreAuthenticationGuardToken $token)
{
// get the user from the GuardAuthenticator
$user = $guardAuthenticator->authenticate($token->getCredentials(), $this->userProvider);
Expand Down Expand Up @@ -101,6 +104,6 @@ private function authenticateViaGuard(GuardAuthenticatorInterface $guardAuthenti

public function supports(TokenInterface $token)
{
return $token instanceof NonAuthenticatedGuardToken;
return $token instanceof GuardTokenInterface;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Guard\Firewall\GuardAuthenticationListener;
use Symfony\Component\Security\Guard\Token\NonAuthenticatedGuardToken;
use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken;
use Symfony\Component\Security\Core\Exception\AuthenticationException;

/**
Expand Down Expand Up @@ -44,7 +44,7 @@ public function testHandleSuccess()

// a clone of the token that should be created internally
$uniqueGuardKey = 'my_firewall_0';
$nonAuthedToken = new NonAuthenticatedGuardToken($credentials, $uniqueGuardKey);
$nonAuthedToken = new PreAuthenticationGuardToken($credentials, $uniqueGuardKey);

$this->authenticationManager
->expects($this->once())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class GuardAuthenticationProviderTest extends \PHPUnit_Framework_TestCase
{
private $userProvider;
private $userChecker;
private $nonAuthedToken;
private $preAuthenticationToken;

public function testAuthenticate()
{
Expand All @@ -32,7 +32,7 @@ public function testAuthenticate()
$authenticators = array($authenticatorA, $authenticatorB, $authenticatorC);

// called 2 times - for authenticator A and B (stops on B because of match)
$this->nonAuthedToken->expects($this->exactly(2))
$this->preAuthenticationToken->expects($this->exactly(2))
->method('getGuardProviderKey')
// it will return the "1" index, which will match authenticatorB
->will($this->returnValue('my_cool_firewall_1'));
Expand All @@ -41,7 +41,7 @@ public function testAuthenticate()
'username' => '_weaverryan_test_user',
'password' => 'guard_auth_ftw',
);
$this->nonAuthedToken->expects($this->once())
$this->preAuthenticationToken->expects($this->once())
->method('getCredentials')
->will($this->returnValue($enteredCredentials));

Expand Down Expand Up @@ -71,15 +71,15 @@ public function testAuthenticate()
->with($mockedUser);

$provider = new GuardAuthenticationProvider($authenticators, $this->userProvider, $providerKey, $this->userChecker);
$actualAuthedToken = $provider->authenticate($this->nonAuthedToken);
$actualAuthedToken = $provider->authenticate($this->preAuthenticationToken);
$this->assertSame($authedToken, $actualAuthedToken);
}

protected function setUp()
{
$this->userProvider = $this->getMock('Symfony\Component\Security\Core\User\UserProviderInterface');
$this->userChecker = $this->getMock('Symfony\Component\Security\Core\User\UserCheckerInterface');
$this->nonAuthedToken = $this->getMockBuilder('Symfony\Component\Security\Guard\Token\NonAuthenticatedGuardToken')
$this->preAuthenticationToken = $this->getMockBuilder('Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken')
->disableOriginalConstructor()
->getMock();
}
Expand All @@ -88,6 +88,6 @@ protected function tearDown()
{
$this->userProvider = null;
$this->userChecker = null;
$this->nonAuthedToken = null;
$this->preAuthenticationToken = null;
}
}
15 changes: 15 additions & 0 deletions 15 src/Symfony/Component/Security/Guard/Token/GuardTokenInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

namespace Symfony\Component\Security\Guard\Token;

/**
* An empty interface that both guard tokens implement
*
* This interface is used by the GuardAuthenticationProvider to know
* that a token belongs to its system.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An interface that knows about its implementations? ;)

What about replacing An empty interface by A marker interface?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call - I updated that and a few other bits of the words here :)

*
* @author Ryan Weaver <weaverryan@gmail.com>
*/
interface GuardTokenInterface
{
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,14 @@
use Symfony\Component\Security\Core\User\UserInterface;

/**
* A generic token used by the AbstractGuardAuthenticator
* Used as an "authenticated" token, though it could be set to not-authenticated later.
*
* This is meant to be used as an "authenticated" token, though it
* could be set to not-authenticated later.
*
* You're free to use this (it's simple) or use any other token for
* your authenticated token
* If you're using Guard authentication, you *must* use a class that implements
* GuardTokenInterface as your authenticated token (like this class).
*
* @author Ryan Weaver <weaverryan@gmail.com>
*/
class GenericGuardToken extends AbstractToken
class PostAuthenticationGuardToken extends AbstractToken implements GuardTokenInterface
{
private $providerKey;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
*
* @author Ryan Weaver <weaverryan@gmail.com>
*/
class NonAuthenticatedGuardToken extends AbstractToken
class PreAuthenticationGuardToken extends AbstractToken implements GuardTokenInterface
{
private $credentials;
private $guardProviderKey;
Expand Down Expand Up @@ -51,6 +51,6 @@ public function getCredentials()

public function setAuthenticated($authenticated)
{
throw new \Exception('The NonAuthenticatedGuardToken is *always* not authenticated');
throw new \Exception('The PreAuthenticationGuardToken is *always* not authenticated');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

throw new \LogicException('The PreAuthenticationGuardToken is *never* authenticated.');

Even if you decide not to change it, the exception should have a dot in its message at the end.

edit: I see more exception messages in the PR that are missing dots

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes, thank you - I agree on both points! I've added dots, so if you see any missing now, please let me know!

}
}
Morty Proxy This is a proxified and sanitized view of the page, visit original site.