├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── Tests
├── Firewall
│ └── GuardAuthenticationListenerTest.php
├── GuardAuthenticatorHandlerTest.php
└── Provider
│ └── GuardAuthenticationProviderTest.php
├── composer.json
├── phpunit.xml.dist
└── src
├── AbstractGuardAuthenticator.php
├── Authenticator
└── AbstractFormLoginAuthenticator.php
├── Exception
└── CustomAuthenticationException.php
├── Firewall
└── GuardAuthenticationListener.php
├── GuardAuthenticatorHandler.php
├── GuardAuthenticatorInterface.php
├── Provider
└── GuardAuthenticationProvider.php
└── Token
├── GuardTokenInterface.php
├── PostAuthenticationGuardToken.php
└── PreAuthenticationGuardToken.php
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor
2 | composer.lock
3 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | php:
4 | - 5.3
5 | - 5.4
6 | - 5.5
7 | - 5.6
8 | - 7.0
9 | - hhvm
10 |
11 | branches:
12 | only:
13 | - master
14 |
15 | matrix:
16 | fast_finish: true
17 | include:
18 | - php: 5.3
19 | env: deps="low"
20 | - php: 5.5
21 | env: SYMFONY_VERSION=2.6.*
22 | - php: 5.5
23 | env: SYMFONY_VERSION='2.7.*@dev'
24 |
25 | cache:
26 | directories:
27 | - $HOME/.composer/cache
28 |
29 | before_script:
30 | - composer self-update
31 | - if [ "$SYMFONY_VERSION" != "" ]; then composer require --dev --no-update symfony/symfony=$SYMFONY_VERSION; fi
32 | - if [ "$deps" = "low" ]; then export COMPOSER_FLAGS='--prefer-lowest'; fi
33 | - composer update $COMPOSER_FLAGS
34 |
35 | script:
36 | - vendor/bin/phpunit
37 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) KnpUniversity
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is furnished
8 | to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # KnpGuard
2 |
3 | Add simple and beautiful authentication to Symfony's security component in Silex
4 | and anywhere else.
5 |
6 | [](https://travis-ci.org/knpuniversity/KnpUGuard)
7 |
8 | **This library is *deprecated* since Symfony 2.8 and won't work with Symfony 3.**
9 |
10 | The original purpose was to get feedback and use-cases from people so that we can merge this feature into Symfony itself
11 | (see [symfony/symfony#14673](https://github.com/symfony/symfony/pull/14673)).
12 |
13 | Now it's good (see [news from Symfony](http://symfony.com/blog/new-in-symfony-2-8-guard-authentication-component)).
14 |
15 | ## Upgrade to Symfony 3
16 |
17 | On Symfony 2.8, use the official [Guard component](https://symfony.com/doc/master/cookbook/security/guard-authentication.html).
18 |
19 | ### Step 1 - Remove the library from your composer.json
20 |
21 | Be sure to be on Symfony 2.8, open `composer.json` file and remove the library:
22 |
23 | Before:
24 | ```json
25 | {
26 | "require": {
27 | "php": ">=5.5",
28 | "symfony/symfony": "~2.8",
29 | "...": "...",
30 | "knpuniversity/guard-bundle": "~0.1@dev"
31 | },
32 | }
33 | ```
34 |
35 | Now:
36 | ```json
37 | {
38 | "require": {
39 | "php": ">=5.5",
40 | "symfony/symfony": "~2.8",
41 | "...": "..."
42 | },
43 | }
44 | ```
45 |
46 | ### Step 2 - Remove it from your AppKernel
47 |
48 | If you're using the Symfony framework, remove the KnpUGuardBundle from `AppKernel.php`.
49 |
50 | ### Step 3 - Modify firewall(s)
51 |
52 | Open and modify `security.yml` file, replace in your firewall(s) key(s) `knpu_guard` by `guard`:
53 |
54 | Before:
55 | ```yaml
56 | # app/config/security.yml
57 | security:
58 | # ...
59 |
60 | firewalls:
61 | # ...
62 |
63 | main:
64 | anonymous: ~
65 | logout: ~
66 |
67 | knpu_guard:
68 | authenticators:
69 | - app.form_login_authenticator
70 |
71 | # maybe other things, like form_login, remember_me, etc
72 | # ...
73 | ```
74 |
75 | Now:
76 | ```yaml
77 | # app/config/security.yml
78 | security:
79 | # ...
80 |
81 | firewalls:
82 | # ...
83 |
84 | main:
85 | anonymous: ~
86 | logout: ~
87 |
88 | guard:
89 | authenticators:
90 | - app.form_login_authenticator
91 |
92 | # maybe other things, like form_login, remember_me, etc
93 | # ...
94 | ```
95 |
96 | ### Step 4 - Update Authenticator(s)
97 |
98 | Update uses in Authenticator(s) class(es).
99 |
100 | **Warning:** checkCredentials() NOW must return true in order for authentication to be successful.
101 | In KnpUGuard, if you did NOT throw an AuthenticationException, it would pass.
102 |
103 | Before:
104 | ```php
105 | use KnpU\Guard\Authenticator\AbstractFormLoginAuthenticator;
106 | use KnpU\Guard\...;
107 | // ...
108 |
109 | class FormLoginAuthenticator extends AbstractFormLoginAuthenticator
110 | {
111 | // ...
112 |
113 | public function checkCredentials($credentials, UserInterface $user)
114 | {
115 | // ...
116 |
117 | if ($password !== 'correctPassword') {
118 | throw new AuthenticationException();
119 | }
120 |
121 | // do nothing, allow authentication to pass
122 | }
123 |
124 | // ...
125 | }
126 | ```
127 |
128 | Now:
129 | ```php
130 | use Symfony\Component\Security\Guard\AbstractFormLoginAuthenticator;
131 | use Symfony\Component\Security\Guard\...;
132 | // ...
133 |
134 | class FormLoginAuthenticator extends AbstractFormLoginAuthenticator
135 | {
136 | // ...
137 |
138 | public function checkCredentials($credentials, UserInterface $user)
139 | {
140 | // ...
141 |
142 | if ($password !== 'correctPassword') {
143 | // returning anything NOT true will cause an authentication failure
144 | return;
145 | // or, you can still throw an AuthenticationException if you want to
146 | // throw new AuthenticationException();
147 | }
148 |
149 | // return true to make authentication successful
150 | return true;
151 | }
152 |
153 | // ...
154 | }
155 | ```
156 |
157 | ### Step 5 - Yes we can test it
158 |
159 | That's it! Try it out, and then upgrade to Symfony 3 :).
160 |
161 | - [http://symfony.com/doc/current/cookbook/upgrade/major_version.html](http://symfony.com/doc/current/cookbook/upgrade/major_version.html)
162 |
163 | ## Documentation
164 |
165 | Find a full tutorial here: https://knpuniversity.com/screencast/guard
166 |
167 | ## Basic Usage
168 |
169 | Check out the [Tutorial](https://knpuniversity.com/screencast/guard) for real documentation.
170 | But here's the basic idea.
171 |
172 | Guard works by creating a single class - an **authenticator** - that handles *everything*
173 | about how you want to authenticate your users. And authenticator implements
174 | [KnpU\Guard\GuardAuthenticatorInterface](https://github.com/knpuniversity/KnpUGuard/blob/master/src/GuardAuthenticatorInterface.php))
175 |
176 | Here are some real-world examples from the tutorial:
177 |
178 | Goal | Code | Tutorial
179 | ------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | -------
180 | Authenticate by reading an `X-TOKEN` header | [ApiTokenAuthenticator.php](https://github.com/knpuniversity/guard-tutorial/blob/finished/src/AppBundle/Security/ApiTokenAuthenticator.php) | [How to Authenticate via an API Token](https://knpuniversity.com/screencast/guard/api-token)
181 | Form login authentication | [FormLoginAuthenticator.php](https://github.com/knpuniversity/guard-tutorial/blob/finished/src/AppBundle/Security/FormLoginAuthenticator.php) | [How to Create a Login Form](https://knpuniversity.com/screencast/guard/login-form)
182 | Social Login (Facebook) | [FacebookAuthenticator.php](https://github.com/knpuniversity/guard-tutorial/blob/finished/src/AppBundle/Security/FacebookAuthenticator.php) | [Social Login with Facebook](https://knpuniversity.com/screencast/guard/social-login)
183 |
184 | ## Contributing
185 |
186 | Find a bug or a use-case that this doesn't support? [Open an Issue](https://github.com/knpuniversity/KnpUGuard/issues)
187 | so we can make things better.
188 |
189 | ## License
190 |
191 | This library is under the MIT license. See the complete license in the LICENSE file.
192 |
--------------------------------------------------------------------------------
/Tests/Firewall/GuardAuthenticationListenerTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace KnpU\Guard\Tests\Firewall;
13 |
14 | use Symfony\Component\HttpFoundation\Request;
15 | use Symfony\Component\HttpFoundation\Response;
16 | use KnpU\Guard\Firewall\GuardAuthenticationListener;
17 | use KnpU\Guard\Token\PreAuthenticationGuardToken;
18 | use Symfony\Component\Security\Core\Exception\AuthenticationException;
19 |
20 | /**
21 | * @author Ryan Weaver
22 | */
23 | class GuardAuthenticationListenerTest extends \PHPUnit_Framework_TestCase
24 | {
25 | private $authenticationManager;
26 | private $guardAuthenticatorHandler;
27 | private $event;
28 | private $logger;
29 | private $request;
30 | private $rememberMeServices;
31 |
32 | public function testHandleSuccess()
33 | {
34 | $authenticator = $this->getMock('KnpU\Guard\GuardAuthenticatorInterface');
35 | $authenticateToken = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
36 | $providerKey = 'my_firewall';
37 |
38 | $credentials = array('username' => 'weaverryan', 'password' => 'all_your_base');
39 | $authenticator
40 | ->expects($this->once())
41 | ->method('getCredentials')
42 | ->with($this->equalTo($this->request))
43 | ->will($this->returnValue($credentials));
44 |
45 | // a clone of the token that should be created internally
46 | $uniqueGuardKey = 'my_firewall_0';
47 | $nonAuthedToken = new PreAuthenticationGuardToken($credentials, $uniqueGuardKey);
48 |
49 | $this->authenticationManager
50 | ->expects($this->once())
51 | ->method('authenticate')
52 | ->with($this->equalTo($nonAuthedToken))
53 | ->will($this->returnValue($authenticateToken));
54 |
55 | $this->guardAuthenticatorHandler
56 | ->expects($this->once())
57 | ->method('authenticateWithToken')
58 | ->with($authenticateToken, $this->request);
59 |
60 | $this->guardAuthenticatorHandler
61 | ->expects($this->once())
62 | ->method('handleAuthenticationSuccess')
63 | ->with($authenticateToken, $this->request, $authenticator, $providerKey);
64 |
65 | $listener = new GuardAuthenticationListener(
66 | $this->guardAuthenticatorHandler,
67 | $this->authenticationManager,
68 | $providerKey,
69 | array($authenticator),
70 | $this->logger
71 | );
72 |
73 | $listener->setRememberMeServices($this->rememberMeServices);
74 | // should never be called - our handleAuthenticationSuccess() does not return a Response
75 | $this->rememberMeServices
76 | ->expects($this->never())
77 | ->method('loginSuccess');
78 |
79 | $listener->handle($this->event);
80 | }
81 |
82 | public function testHandleSuccessWithRememberMe()
83 | {
84 | $authenticator = $this->getMock('KnpU\Guard\GuardAuthenticatorInterface');
85 | $authenticateToken = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
86 | $providerKey = 'my_firewall_with_rememberme';
87 |
88 | $authenticator
89 | ->expects($this->once())
90 | ->method('getCredentials')
91 | ->with($this->equalTo($this->request))
92 | ->will($this->returnValue(array('username' => 'anything_not_empty')));
93 |
94 | $this->authenticationManager
95 | ->expects($this->once())
96 | ->method('authenticate')
97 | ->will($this->returnValue($authenticateToken));
98 |
99 | $successResponse = new Response('Success!');
100 | $this->guardAuthenticatorHandler
101 | ->expects($this->once())
102 | ->method('handleAuthenticationSuccess')
103 | ->will($this->returnValue($successResponse));
104 |
105 | $listener = new GuardAuthenticationListener(
106 | $this->guardAuthenticatorHandler,
107 | $this->authenticationManager,
108 | $providerKey,
109 | array($authenticator),
110 | $this->logger
111 | );
112 |
113 | $listener->setRememberMeServices($this->rememberMeServices);
114 | $authenticator->expects($this->once())
115 | ->method('supportsRememberMe')
116 | ->will($this->returnValue(true));
117 | // should be called - we do have a success Response
118 | $this->rememberMeServices
119 | ->expects($this->once())
120 | ->method('loginSuccess');
121 |
122 | $listener->handle($this->event);
123 | }
124 |
125 | public function testHandleCatchesAuthenticationException()
126 | {
127 | $authenticator = $this->getMock('KnpU\Guard\GuardAuthenticatorInterface');
128 | $providerKey = 'my_firewall2';
129 |
130 | $authException = new AuthenticationException('Get outta here crazy user with a bad password!');
131 | $authenticator
132 | ->expects($this->once())
133 | ->method('getCredentials')
134 | ->will($this->throwException($authException));
135 |
136 | // this is not called
137 | $this->authenticationManager
138 | ->expects($this->never())
139 | ->method('authenticate');
140 |
141 | $this->guardAuthenticatorHandler
142 | ->expects($this->once())
143 | ->method('handleAuthenticationFailure')
144 | ->with($authException, $this->request, $authenticator);
145 |
146 | $listener = new GuardAuthenticationListener(
147 | $this->guardAuthenticatorHandler,
148 | $this->authenticationManager,
149 | $providerKey,
150 | array($authenticator),
151 | $this->logger
152 | );
153 |
154 | $listener->handle($this->event);
155 | }
156 |
157 | public function testReturnNullToSkipAuth()
158 | {
159 | $authenticatorA = $this->getMock('KnpU\Guard\GuardAuthenticatorInterface');
160 | $authenticatorB = $this->getMock('KnpU\Guard\GuardAuthenticatorInterface');
161 | $providerKey = 'my_firewall3';
162 |
163 | $authenticatorA
164 | ->expects($this->once())
165 | ->method('getCredentials')
166 | ->will($this->returnValue(null));
167 | $authenticatorB
168 | ->expects($this->once())
169 | ->method('getCredentials')
170 | ->will($this->returnValue(null));
171 |
172 | // this is not called
173 | $this->authenticationManager
174 | ->expects($this->never())
175 | ->method('authenticate');
176 |
177 | $this->guardAuthenticatorHandler
178 | ->expects($this->never())
179 | ->method('handleAuthenticationSuccess');
180 |
181 | $listener = new GuardAuthenticationListener(
182 | $this->guardAuthenticatorHandler,
183 | $this->authenticationManager,
184 | $providerKey,
185 | array($authenticatorA, $authenticatorB),
186 | $this->logger
187 | );
188 |
189 | $listener->handle($this->event);
190 | }
191 |
192 | protected function setUp()
193 | {
194 | $this->authenticationManager = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager')
195 | ->disableOriginalConstructor()
196 | ->getMock();
197 |
198 | $this->guardAuthenticatorHandler = $this->getMockBuilder('KnpU\Guard\GuardAuthenticatorHandler')
199 | ->disableOriginalConstructor()
200 | ->getMock();
201 |
202 | $this->request = new Request(array(), array(), array(), array(), array(), array());
203 |
204 | $this->event = $this->getMock('Symfony\Component\HttpKernel\Event\GetResponseEvent', array(), array(), '', false);
205 | $this->event
206 | ->expects($this->any())
207 | ->method('getRequest')
208 | ->will($this->returnValue($this->request));
209 |
210 | $this->logger = $this->getMock('Psr\Log\LoggerInterface');
211 | $this->rememberMeServices = $this->getMock('Symfony\Component\Security\Http\RememberMe\RememberMeServicesInterface');
212 | }
213 |
214 | protected function tearDown()
215 | {
216 | $this->authenticationManager = null;
217 | $this->guardAuthenticatorHandler = null;
218 | $this->event = null;
219 | $this->logger = null;
220 | $this->request = null;
221 | }
222 | }
223 |
--------------------------------------------------------------------------------
/Tests/GuardAuthenticatorHandlerTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace KnpU\Guard\Tests;
13 |
14 | use Symfony\Component\HttpFoundation\Request;
15 | use Symfony\Component\HttpFoundation\Response;
16 | use KnpU\Guard\GuardAuthenticatorHandler;
17 | use Symfony\Component\Security\Core\Exception\AuthenticationException;
18 | use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
19 | use Symfony\Component\Security\Http\SecurityEvents;
20 |
21 | /**
22 | * @author Ryan Weaver
23 | */
24 | class GuardAuthenticatorHandlerTest extends \PHPUnit_Framework_TestCase
25 | {
26 | private $tokenStorage;
27 | private $dispatcher;
28 | private $token;
29 | private $request;
30 | private $guardAuthenticator;
31 |
32 | public function testAuthenticateWithToken()
33 | {
34 | $this->tokenStorage->expects($this->once())
35 | ->method('setToken')
36 | ->with($this->token);
37 |
38 | $loginEvent = new InteractiveLoginEvent($this->request, $this->token);
39 |
40 | $this->dispatcher
41 | ->expects($this->once())
42 | ->method('dispatch')
43 | ->with($this->equalTo(SecurityEvents::INTERACTIVE_LOGIN), $this->equalTo($loginEvent))
44 | ;
45 |
46 | $handler = new GuardAuthenticatorHandler($this->tokenStorage, $this->dispatcher);
47 | $handler->authenticateWithToken($this->token, $this->request);
48 | }
49 |
50 | public function testHandleAuthenticationSuccess()
51 | {
52 | $providerKey = 'my_handleable_firewall';
53 | $response = new Response('Guard all the things!');
54 | $this->guardAuthenticator->expects($this->once())
55 | ->method('onAuthenticationSuccess')
56 | ->with($this->request, $this->token, $providerKey)
57 | ->will($this->returnValue($response));
58 |
59 | $handler = new GuardAuthenticatorHandler($this->tokenStorage, $this->dispatcher);
60 | $actualResponse = $handler->handleAuthenticationSuccess($this->token, $this->request, $this->guardAuthenticator, $providerKey);
61 | $this->assertSame($response, $actualResponse);
62 | }
63 |
64 | public function testHandleAuthenticationFailure()
65 | {
66 | $this->tokenStorage->expects($this->once())
67 | ->method('setToken')
68 | ->with(null);
69 | $authException = new AuthenticationException('Bad password!');
70 |
71 | $response = new Response('Try again, but with the right password!');
72 | $this->guardAuthenticator->expects($this->once())
73 | ->method('onAuthenticationFailure')
74 | ->with($this->request, $authException)
75 | ->will($this->returnValue($response));
76 |
77 | $handler = new GuardAuthenticatorHandler($this->tokenStorage, $this->dispatcher);
78 | $actualResponse = $handler->handleAuthenticationFailure($authException, $this->request, $this->guardAuthenticator);
79 | $this->assertSame($response, $actualResponse);
80 | }
81 |
82 | protected function setUp()
83 | {
84 | $this->tokenStorage = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface');
85 | $this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
86 | $this->token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
87 | $this->request = new Request(array(), array(), array(), array(), array(), array());
88 | $this->guardAuthenticator = $this->getMock('KnpU\Guard\GuardAuthenticatorInterface');
89 | }
90 |
91 | protected function tearDown()
92 | {
93 | $this->tokenStorage = null;
94 | $this->dispatcher = null;
95 | $this->token = null;
96 | $this->request = null;
97 | $this->guardAuthenticator = null;
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/Tests/Provider/GuardAuthenticationProviderTest.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace KnpU\Guard\Tests\Provider;
13 |
14 | use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken;
15 | use KnpU\Guard\Provider\GuardAuthenticationProvider;
16 | use KnpU\Guard\Token\PostAuthenticationGuardToken;
17 |
18 | /**
19 | * @author Ryan Weaver
20 | */
21 | class GuardAuthenticationProviderTest extends \PHPUnit_Framework_TestCase
22 | {
23 | private $userProvider;
24 | private $userChecker;
25 | private $preAuthenticationToken;
26 |
27 | public function testAuthenticate()
28 | {
29 | $providerKey = 'my_cool_firewall';
30 |
31 | $authenticatorA = $this->getMock('KnpU\Guard\GuardAuthenticatorInterface');
32 | $authenticatorB = $this->getMock('KnpU\Guard\GuardAuthenticatorInterface');
33 | $authenticatorC = $this->getMock('KnpU\Guard\GuardAuthenticatorInterface');
34 | $authenticators = array($authenticatorA, $authenticatorB, $authenticatorC);
35 |
36 | // called 2 times - for authenticator A and B (stops on B because of match)
37 | $this->preAuthenticationToken->expects($this->exactly(2))
38 | ->method('getGuardProviderKey')
39 | // it will return the "1" index, which will match authenticatorB
40 | ->will($this->returnValue('my_cool_firewall_1'));
41 |
42 | $enteredCredentials = array(
43 | 'username' => '_weaverryan_test_user',
44 | 'password' => 'guard_auth_ftw',
45 | );
46 | $this->preAuthenticationToken->expects($this->atLeastOnce())
47 | ->method('getCredentials')
48 | ->will($this->returnValue($enteredCredentials));
49 |
50 | // authenticators A and C are never called
51 | $authenticatorA->expects($this->never())
52 | ->method('getUser');
53 | $authenticatorC->expects($this->never())
54 | ->method('getUser');
55 |
56 | $mockedUser = $this->getMock('Symfony\Component\Security\Core\User\UserInterface');
57 | $authenticatorB->expects($this->once())
58 | ->method('getUser')
59 | ->with($enteredCredentials, $this->userProvider)
60 | ->will($this->returnValue($mockedUser));
61 | // checkCredentials is called
62 | $authenticatorB->expects($this->once())
63 | ->method('checkCredentials')
64 | ->with($enteredCredentials, $mockedUser);
65 | $authedToken = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
66 | $authenticatorB->expects($this->once())
67 | ->method('createAuthenticatedToken')
68 | ->with($mockedUser, $providerKey)
69 | ->will($this->returnValue($authedToken));
70 |
71 | // user checker should be called
72 | $this->userChecker->expects($this->once())
73 | ->method('checkPreAuth')
74 | ->with($mockedUser);
75 | $this->userChecker->expects($this->once())
76 | ->method('checkPostAuth')
77 | ->with($mockedUser);
78 |
79 | $provider = new GuardAuthenticationProvider($authenticators, $this->userProvider, $providerKey, $this->userChecker);
80 | $actualAuthedToken = $provider->authenticate($this->preAuthenticationToken);
81 | $this->assertSame($authedToken, $actualAuthedToken);
82 | }
83 |
84 | public function testGuardWithNoLongerAuthenticatedTriggersLogout()
85 | {
86 | $providerKey = 'my_firewall_abc';
87 |
88 | // create a token and mark it as NOT authenticated anymore
89 | // this mimics what would happen if a user "changed" between request
90 | $mockedUser = $this->getMock('Symfony\Component\Security\Core\User\UserInterface');
91 | $token = new PostAuthenticationGuardToken($mockedUser, $providerKey, array('ROLE_USER'));
92 | $token->setAuthenticated(false);
93 |
94 | $provider = new GuardAuthenticationProvider(array(), $this->userProvider, $providerKey, $this->userChecker);
95 | $actualToken = $provider->authenticate($token);
96 | // this should return the anonymous user
97 | $this->assertEquals(new AnonymousToken($providerKey, 'anon.'), $actualToken);
98 | }
99 |
100 | protected function setUp()
101 | {
102 | $this->userProvider = $this->getMock('Symfony\Component\Security\Core\User\UserProviderInterface');
103 | $this->userChecker = $this->getMock('Symfony\Component\Security\Core\User\UserCheckerInterface');
104 | $this->preAuthenticationToken = $this->getMockBuilder('KnpU\Guard\Token\PreAuthenticationGuardToken')
105 | ->disableOriginalConstructor()
106 | ->getMock();
107 | }
108 |
109 | protected function tearDown()
110 | {
111 | $this->userProvider = null;
112 | $this->userChecker = null;
113 | $this->preAuthenticationToken = null;
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "knpuniversity/guard",
3 | "description": "Provides Guard-style authentication in Symfony's security component",
4 | "keywords": ["security"],
5 | "homepage": "http://knpuniversity.com",
6 | "license": "MIT",
7 | "authors": [
8 | {
9 | "name": "Ryan Weaver",
10 | "email": "weaverryan@gmail.com"
11 | }
12 | ],
13 | "require": {
14 | "php": ">=5.3.9",
15 | "symfony/security-core": "~2.6",
16 | "symfony/security-http": "~2.6"
17 | },
18 | "require-dev": {
19 | "phpunit/phpunit": "~4.7"
20 | },
21 | "autoload": {
22 | "psr-4": { "KnpU\\Guard\\": "src/" }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ./Tests/
7 |
8 |
9 |
10 |
11 |
12 | ./
13 |
14 | ./Resources
15 | ./Tests
16 | ./vendor
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/AbstractGuardAuthenticator.php:
--------------------------------------------------------------------------------
1 |
12 | */
13 | abstract class AbstractGuardAuthenticator implements GuardAuthenticatorInterface
14 | {
15 | /**
16 | * Shortcut to create a PostAuthenticationGuardToken for you, if you don't really
17 | * care about which authenticated token you're using.
18 | *
19 | * @param UserInterface $user
20 | * @param string $providerKey
21 | *
22 | * @return PostAuthenticationGuardToken
23 | */
24 | public function createAuthenticatedToken(UserInterface $user, $providerKey)
25 | {
26 | return new PostAuthenticationGuardToken(
27 | $user,
28 | $providerKey,
29 | $user->getRoles()
30 | );
31 | }
32 | }
--------------------------------------------------------------------------------
/src/Authenticator/AbstractFormLoginAuthenticator.php:
--------------------------------------------------------------------------------
1 | getSession()->set(Security::AUTHENTICATION_ERROR, $exception);
48 | $url = $this->getLoginUrl();
49 |
50 | return new RedirectResponse($url);
51 | }
52 |
53 | /**
54 | * Override to change what happens after successful authentication
55 | *
56 | * @param Request $request
57 | * @param TokenInterface $token
58 | * @param string $providerKey
59 | * @return RedirectResponse
60 | */
61 | public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
62 | {
63 | // if the user hit a secure page and start() was called, this was
64 | // the URL they were on, and probably where you want to redirect to
65 | $targetPath = $request->getSession()->get('_security.'.$providerKey.'.target_path');
66 |
67 | if (!$targetPath) {
68 | $targetPath = $this->getDefaultSuccessRedirectUrl();
69 | }
70 |
71 | return new RedirectResponse($targetPath);
72 | }
73 |
74 | public function supportsRememberMe()
75 | {
76 | return true;
77 | }
78 |
79 | /**
80 | * Override to control what happens when the user hits a secure page
81 | * but isn't logged in yet.
82 | *
83 | * @param Request $request
84 | * @param AuthenticationException|null $authException
85 | * @return RedirectResponse
86 | */
87 | public function start(Request $request, AuthenticationException $authException = null)
88 | {
89 | $url = $this->getLoginUrl();
90 |
91 | return new RedirectResponse($url);
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/src/Exception/CustomAuthenticationException.php:
--------------------------------------------------------------------------------
1 | setSafeMessage($safeMessage, $messageData);
35 |
36 | return $exception;
37 | }
38 |
39 | /**
40 | * Set a message that will be shown to the user
41 | *
42 | * @param string $messageKey The message or message key
43 | * @param array $messageData Data to be passed into the translator
44 | */
45 | public function setSafeMessage($messageKey, array $messageData)
46 | {
47 | $this->messageKey = $messageKey;
48 | $this->messageData = $messageData;
49 | }
50 |
51 | public function getMessageKey()
52 | {
53 | return $this->messageKey !== null ? $this->messageKey : parent::getMessageKey();
54 | }
55 |
56 | public function getMessageData()
57 | {
58 | return $this->messageData !== null ? $this->messageData : parent::getMessageData();
59 | }
60 |
61 | /**
62 | * {@inheritdoc}
63 | */
64 | public function serialize()
65 | {
66 | return serialize(array(
67 | $this->messageKey,
68 | $this->messageData,
69 | parent::serialize(),
70 | ));
71 | }
72 |
73 | /**
74 | * {@inheritdoc}
75 | */
76 | public function unserialize($str)
77 | {
78 | list($this->messageKey, $this->messageData, $parentData) = unserialize($str);
79 |
80 | parent::unserialize($parentData);
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/Firewall/GuardAuthenticationListener.php:
--------------------------------------------------------------------------------
1 |
22 | */
23 | class GuardAuthenticationListener implements ListenerInterface
24 | {
25 | private $guardHandler;
26 | private $authenticationManager;
27 | private $providerKey;
28 | private $guardAuthenticators;
29 | private $logger;
30 | private $rememberMeServices;
31 |
32 | /**
33 | * @param GuardAuthenticatorHandler $guardHandler The Guard handler
34 | * @param AuthenticationManagerInterface $authenticationManager An AuthenticationManagerInterface instance
35 | * @param string $providerKey The provider (i.e. firewall) key
36 | * @param GuardAuthenticatorInterface[] $guardAuthenticators The authenticators, with keys that match what's passed to GuardAuthenticationProvider
37 | * @param LoggerInterface $logger A LoggerInterface instance
38 | */
39 | public function __construct(GuardAuthenticatorHandler $guardHandler, AuthenticationManagerInterface $authenticationManager, $providerKey, array $guardAuthenticators, LoggerInterface $logger = null)
40 | {
41 | if (empty($providerKey)) {
42 | throw new \InvalidArgumentException('$providerKey must not be empty.');
43 | }
44 |
45 | $this->guardHandler = $guardHandler;
46 | $this->authenticationManager = $authenticationManager;
47 | $this->providerKey = $providerKey;
48 | $this->guardAuthenticators = $guardAuthenticators;
49 | $this->logger = $logger;
50 | }
51 |
52 | /**
53 | * Iterates over each authenticator to see if each wants to authenticate the request.
54 | *
55 | * @param GetResponseEvent $event
56 | */
57 | public function handle(GetResponseEvent $event)
58 | {
59 | if (null !== $this->logger) {
60 | $this->logger->info('Checking for guard authentication credentials.', array('firewall_key' => $this->providerKey, 'authenticators' => count($this->guardAuthenticators)));
61 | }
62 |
63 | foreach ($this->guardAuthenticators as $key => $guardAuthenticator) {
64 | // get a key that's unique to *this* guard authenticator
65 | // this MUST be the same as GuardAuthenticationProvider
66 | $uniqueGuardKey = $this->providerKey.'_'.$key;
67 |
68 | $this->executeGuardAuthenticator($uniqueGuardKey, $guardAuthenticator, $event);
69 | }
70 | }
71 |
72 | private function executeGuardAuthenticator($uniqueGuardKey, GuardAuthenticatorInterface $guardAuthenticator, GetResponseEvent $event)
73 | {
74 | $request = $event->getRequest();
75 | try {
76 | if (null !== $this->logger) {
77 | $this->logger->info('Calling getCredentials on guard configurator.', array('firewall_key' => $this->providerKey, 'authenticator' => get_class($guardAuthenticator)));
78 | }
79 |
80 | // allow the authenticator to fetch authentication info from the request
81 | $credentials = $guardAuthenticator->getCredentials($request);
82 |
83 | // allow null to be returned to skip authentication
84 | if (null === $credentials) {
85 | return;
86 | }
87 |
88 | // create a token with the unique key, so that the provider knows which authenticator to use
89 | $token = new PreAuthenticationGuardToken($credentials, $uniqueGuardKey);
90 |
91 | if (null !== $this->logger) {
92 | $this->logger->info('Passing guard token information to the GuardAuthenticationProvider', array('firewall_key' => $this->providerKey, 'authenticator' => get_class($guardAuthenticator)));
93 | }
94 | // pass the token into the AuthenticationManager system
95 | // this indirectly calls GuardAuthenticationProvider::authenticate()
96 | $token = $this->authenticationManager->authenticate($token);
97 |
98 | if (null !== $this->logger) {
99 | $this->logger->info('Guard authentication successful!', array('token' => $token, 'authenticator' => get_class($guardAuthenticator)));
100 | }
101 |
102 | // sets the token on the token storage, etc
103 | $this->guardHandler->authenticateWithToken($token, $request);
104 | } catch (AuthenticationException $e) {
105 | // oh no! Authentication failed!
106 |
107 | if (null !== $this->logger) {
108 | $this->logger->info('Guard authentication failed.', array('exception' => $e, 'authenticator' => get_class($guardAuthenticator)));
109 | }
110 |
111 | $response = $this->guardHandler->handleAuthenticationFailure($e, $request, $guardAuthenticator);
112 |
113 | if ($response instanceof Response) {
114 | $event->setResponse($response);
115 | }
116 |
117 | return;
118 | }
119 |
120 | // success!
121 | $response = $this->guardHandler->handleAuthenticationSuccess($token, $request, $guardAuthenticator, $this->providerKey);
122 | if ($response instanceof Response) {
123 | if (null !== $this->logger) {
124 | $this->logger->info('Guard authenticator set success response.', array('response' => $response, 'authenticator' => get_class($guardAuthenticator)));
125 | }
126 |
127 | $event->setResponse($response);
128 | } else {
129 | if (null !== $this->logger) {
130 | $this->logger->info('Guard authenticator set no success response: request continues.', array('authenticator' => get_class($guardAuthenticator)));
131 | }
132 | }
133 |
134 | // attempt to trigger the remember me functionality
135 | $this->triggerRememberMe($guardAuthenticator, $request, $token, $response);
136 | }
137 |
138 | /**
139 | * Should be called if this listener will support remember me.
140 | *
141 | * @param RememberMeServicesInterface $rememberMeServices
142 | */
143 | public function setRememberMeServices(RememberMeServicesInterface $rememberMeServices)
144 | {
145 | $this->rememberMeServices = $rememberMeServices;
146 | }
147 |
148 | /**
149 | * Checks to see if remember me is supported in the authenticator and
150 | * on the firewall. If it is, the RememberMeServicesInterface is notified.
151 | *
152 | * @param GuardAuthenticatorInterface $guardAuthenticator
153 | * @param Request $request
154 | * @param TokenInterface $token
155 | * @param Response $response
156 | */
157 | private function triggerRememberMe(GuardAuthenticatorInterface $guardAuthenticator, Request $request, TokenInterface $token, Response $response = null)
158 | {
159 | if (!$guardAuthenticator->supportsRememberMe()) {
160 | return;
161 | }
162 |
163 | if (null === $this->rememberMeServices) {
164 | if (null !== $this->logger) {
165 | $this->logger->info('Remember me skipped: it is not configured for the firewall.', array('authenticator' => get_class($guardAuthenticator)));
166 | }
167 |
168 | return;
169 | }
170 |
171 | if (!$response instanceof Response) {
172 | throw new \LogicException(sprintf(
173 | '%s::onAuthenticationSuccess *must* return a Response if you want to use the remember me functionality. Return a Response, or set remember_me to false under the guard configuration.',
174 | get_class($guardAuthenticator)
175 | ));
176 | }
177 |
178 | $this->rememberMeServices->loginSuccess($request, $response, $token);
179 | }
180 | }
--------------------------------------------------------------------------------
/src/GuardAuthenticatorHandler.php:
--------------------------------------------------------------------------------
1 |
22 | */
23 | class GuardAuthenticatorHandler
24 | {
25 | private $tokenStorage;
26 |
27 | private $dispatcher;
28 |
29 | public function __construct(TokenStorageInterface $tokenStorage, EventDispatcherInterface $eventDispatcher = null)
30 | {
31 | $this->tokenStorage = $tokenStorage;
32 | $this->dispatcher = $eventDispatcher;
33 | }
34 |
35 | /**
36 | * Authenticates the given token in the system.
37 | *
38 | * @param TokenInterface $token
39 | * @param Request $request
40 | */
41 | public function authenticateWithToken(TokenInterface $token, Request $request)
42 | {
43 | $this->tokenStorage->setToken($token);
44 |
45 | if (null !== $this->dispatcher) {
46 | $loginEvent = new InteractiveLoginEvent($request, $token);
47 | $this->dispatcher->dispatch(SecurityEvents::INTERACTIVE_LOGIN, $loginEvent);
48 | }
49 | }
50 |
51 | /**
52 | * Returns the "on success" response for the given GuardAuthenticator.
53 | *
54 | * @param TokenInterface $token
55 | * @param Request $request
56 | * @param GuardAuthenticatorInterface $guardAuthenticator
57 | * @param string $providerKey The provider (i.e. firewall) key
58 | *
59 | * @return null|Response
60 | */
61 | public function handleAuthenticationSuccess(TokenInterface $token, Request $request, GuardAuthenticatorInterface $guardAuthenticator, $providerKey)
62 | {
63 | $response = $guardAuthenticator->onAuthenticationSuccess($request, $token, $providerKey);
64 |
65 | // check that it's a Response or null
66 | if ($response instanceof Response || null === $response) {
67 | return $response;
68 | }
69 |
70 | throw new \UnexpectedValueException(sprintf(
71 | 'The %s::onAuthenticationSuccess method must return null or a Response object. You returned %s.',
72 | get_class($guardAuthenticator),
73 | is_object($response) ? get_class($response) : gettype($response)
74 | ));
75 | }
76 |
77 | /**
78 | * Convenience method for authenticating the user and returning the
79 | * Response *if any* for success.
80 | *
81 | * @param UserInterface $user
82 | * @param Request $request
83 | * @param GuardAuthenticatorInterface $authenticator
84 | * @param string $providerKey The provider (i.e. firewall) key
85 | *
86 | * @return Response|null
87 | */
88 | public function authenticateUserAndHandleSuccess(UserInterface $user, Request $request, GuardAuthenticatorInterface $authenticator, $providerKey)
89 | {
90 | // create an authenticated token for the User
91 | $token = $authenticator->createAuthenticatedToken($user, $providerKey);
92 | // authenticate this in the system
93 | $this->authenticateWithToken($token, $request);
94 |
95 | // return the success metric
96 | return $this->handleAuthenticationSuccess($token, $request, $authenticator, $providerKey);
97 | }
98 |
99 | /**
100 | * Handles an authentication failure and returns the Response for the
101 | * GuardAuthenticator.
102 | *
103 | * @param AuthenticationException $authenticationException
104 | * @param Request $request
105 | * @param GuardAuthenticatorInterface $guardAuthenticator
106 | *
107 | * @return null|Response
108 | */
109 | public function handleAuthenticationFailure(AuthenticationException $authenticationException, Request $request, GuardAuthenticatorInterface $guardAuthenticator)
110 | {
111 | $this->tokenStorage->setToken(null);
112 |
113 | $response = $guardAuthenticator->onAuthenticationFailure($request, $authenticationException);
114 | if ($response instanceof Response || null === $response) {
115 | // returning null is ok, it means they want the request to continue
116 | return $response;
117 | }
118 |
119 | throw new \UnexpectedValueException(sprintf(
120 | 'The %s::onAuthenticationFailure method must return null or a Response object. You returned %s.',
121 | get_class($guardAuthenticator),
122 | is_object($response) ? get_class($response) : gettype($response)
123 | ));
124 | }
125 | }
--------------------------------------------------------------------------------
/src/GuardAuthenticatorInterface.php:
--------------------------------------------------------------------------------
1 |
22 | */
23 | interface GuardAuthenticatorInterface extends AuthenticationEntryPointInterface
24 | {
25 | /**
26 | * Get the authentication credentials from the request and return them
27 | * as any type (e.g. an associate array). If you return null, authentication
28 | * will be skipped.
29 | *
30 | * Whatever value you return here will be passed to getUser() and checkCredentials()
31 | *
32 | * For example, for a form login, you might:
33 | *
34 | * return array(
35 | * 'username' => $request->request->get('_username'),
36 | * 'password' => $request->request->get('_password'),
37 | * );
38 | *
39 | * Or for an API token that's on a header, you might use:
40 | *
41 | * return array('api_key' => $request->headers->get('X-API-TOKEN'));
42 | *
43 | * @param Request $request
44 | *
45 | * @return mixed|null
46 | */
47 | public function getCredentials(Request $request);
48 |
49 | /**
50 | * Return a UserInterface object based on the credentials.
51 | *
52 | * The *credentials* are the return value from getCredentials()
53 | *
54 | * You may throw an AuthenticationException if you wish. If you return
55 | * null, then a UsernameNotFoundException is thrown for you.
56 | *
57 | * @param mixed $credentials
58 | * @param UserProviderInterface $userProvider
59 | *
60 | * @throws AuthenticationException
61 | *
62 | * @return UserInterface|null
63 | */
64 | public function getUser($credentials, UserProviderInterface $userProvider);
65 |
66 | /**
67 | * Throw an AuthenticationException if the credentials are invalid.
68 | *
69 | * The *credentials* are the return value from getCredentials()
70 | *
71 | * @param mixed $credentials
72 | * @param UserInterface $user
73 | *
74 | * @throws AuthenticationException
75 | */
76 | public function checkCredentials($credentials, UserInterface $user);
77 |
78 | /**
79 | * Create an authenticated token for the given user.
80 | *
81 | * If you don't care about which token class is used or don't really
82 | * understand what a "token" is, you can skip this method by extending
83 | * the AbstractGuardAuthenticator class from your authenticator.
84 | *
85 | * @see AbstractGuardAuthenticator
86 | *
87 | * @param UserInterface $user
88 | * @param string $providerKey The provider (i.e. firewall) key
89 | *
90 | * @return GuardTokenInterface
91 | */
92 | public function createAuthenticatedToken(UserInterface $user, $providerKey);
93 |
94 | /**
95 | * Called when authentication executed, but failed (e.g. wrong username password).
96 | *
97 | * This should return the Response sent back to the user, like a
98 | * RedirectResponse to the login page or a 403 response.
99 | *
100 | * If you return null, the request will continue, but the user will
101 | * not be authenticated. This is probably not what you want to do.
102 | *
103 | * @param Request $request
104 | * @param AuthenticationException $exception
105 | *
106 | * @return Response|null
107 | */
108 | public function onAuthenticationFailure(Request $request, AuthenticationException $exception);
109 |
110 | /**
111 | * Called when authentication executed and was successful!
112 | *
113 | * This should return the Response sent back to the user, like a
114 | * RedirectResponse to the last page they visited.
115 | *
116 | * If you return null, the current request will continue, and the user
117 | * will be authenticated. This makes sense, for example, with an API.
118 | *
119 | * @param Request $request
120 | * @param TokenInterface $token
121 | * @param string $providerKey The provider (i.e. firewall) key
122 | *
123 | * @return Response|null
124 | */
125 | public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey);
126 |
127 | /**
128 | * Does this method support remember me cookies?
129 | *
130 | * Remember me cookie will be set if *all* of the following are met:
131 | * A) This method returns true
132 | * B) The remember_me key under your firewall is configured
133 | * C) The "remember me" functionality is activated. This is usually
134 | * done by having a _remember_me checkbox in your form, but
135 | * can be configured by the "always_remember_me" and "remember_me_parameter"
136 | * parameters under the "remember_me" firewall key
137 | *
138 | * @return bool
139 | */
140 | public function supportsRememberMe();
141 | }
142 |
--------------------------------------------------------------------------------
/src/Provider/GuardAuthenticationProvider.php:
--------------------------------------------------------------------------------
1 |
21 | */
22 | class GuardAuthenticationProvider implements AuthenticationProviderInterface
23 | {
24 | /**
25 | * @var GuardAuthenticatorInterface[]
26 | */
27 | private $guardAuthenticators;
28 | private $userProvider;
29 | private $providerKey;
30 | private $userChecker;
31 |
32 | /**
33 | * @param GuardAuthenticatorInterface[] $guardAuthenticators The authenticators, with keys that match what's passed to GuardAuthenticationListener
34 | * @param UserProviderInterface $userProvider The user provider
35 | * @param string $providerKey The provider (i.e. firewall) key
36 | * @param UserCheckerInterface $userChecker
37 | */
38 | public function __construct(array $guardAuthenticators, UserProviderInterface $userProvider, $providerKey, UserCheckerInterface $userChecker)
39 | {
40 | $this->guardAuthenticators = $guardAuthenticators;
41 | $this->userProvider = $userProvider;
42 | $this->providerKey = $providerKey;
43 | $this->userChecker = $userChecker;
44 | }
45 |
46 | /**
47 | * Finds the correct authenticator for the token and calls it.
48 | *
49 | * @param GuardTokenInterface $token
50 | *
51 | * @return TokenInterface
52 | */
53 | public function authenticate(TokenInterface $token)
54 | {
55 | if (!$this->supports($token)) {
56 | throw new \InvalidArgumentException('GuardAuthenticationProvider only supports GuardTokenInterface.');
57 | }
58 |
59 | if (!$token instanceof PreAuthenticationGuardToken) {
60 | /*
61 | * The listener *only* passes PreAuthenticationGuardToken instances.
62 | * This means that an authenticated token (e.g. PostAuthenticationGuardToken)
63 | * is being passed here, which happens if that token becomes
64 | * "not authenticated" (e.g. happens if the user changes between
65 | * requests). In this case, the user should be logged out, so
66 | * we will return an AnonymousToken to accomplish that.
67 | */
68 |
69 | // this should never happen - but technically, the token is
70 | // authenticated... so it could just be returned
71 | if ($token->isAuthenticated()) {
72 | return $token;
73 | }
74 |
75 | // cause the logout - the token is not authenticated
76 | return new AnonymousToken($this->providerKey, 'anon.');
77 | }
78 |
79 | // find the *one* GuardAuthenticator that this token originated from
80 | foreach ($this->guardAuthenticators as $key => $guardAuthenticator) {
81 | // get a key that's unique to *this* guard authenticator
82 | // this MUST be the same as GuardAuthenticationListener
83 | $uniqueGuardKey = $this->providerKey.'_'.$key;
84 |
85 | if ($uniqueGuardKey == $token->getGuardProviderKey()) {
86 | return $this->authenticateViaGuard($guardAuthenticator, $token);
87 | }
88 | }
89 |
90 | // no matching authenticator found - but there will be multiple GuardAuthenticationProvider
91 | // instances that will be checked if you have multiple firewalls.
92 | }
93 |
94 | private function authenticateViaGuard(GuardAuthenticatorInterface $guardAuthenticator, PreAuthenticationGuardToken $token)
95 | {
96 | // get the user from the GuardAuthenticator
97 | $user = $guardAuthenticator->getUser($token->getCredentials(), $this->userProvider);
98 |
99 | if (null === $user) {
100 | throw new UsernameNotFoundException(sprintf(
101 | 'Null returned from %s::getUser()',
102 | get_class($guardAuthenticator)
103 | ));
104 | }
105 |
106 | if (!$user instanceof UserInterface) {
107 | throw new \UnexpectedValueException(sprintf(
108 | 'The %s::getUser method must return a UserInterface. You returned %s.',
109 | get_class($guardAuthenticator),
110 | is_object($user) ? get_class($user) : gettype($user)
111 | ));
112 | }
113 |
114 | // check the preAuth UserChecker
115 | $this->userChecker->checkPreAuth($user);
116 | // check the credentials
117 | $guardAuthenticator->checkCredentials($token->getCredentials(), $user);
118 | // check the postAuth UserChecker
119 | $this->userChecker->checkPostAuth($user);
120 |
121 | // turn the UserInterface into a TokenInterface
122 | $authenticatedToken = $guardAuthenticator->createAuthenticatedToken($user, $this->providerKey);
123 | if (!$authenticatedToken instanceof TokenInterface) {
124 | throw new \UnexpectedValueException(sprintf(
125 | 'The %s::createAuthenticatedToken method must return a TokenInterface. You returned %s.',
126 | get_class($guardAuthenticator),
127 | is_object($authenticatedToken) ? get_class($authenticatedToken) : gettype($authenticatedToken)
128 | ));
129 | }
130 |
131 | return $authenticatedToken;
132 | }
133 |
134 | public function supports(TokenInterface $token)
135 | {
136 | return $token instanceof GuardTokenInterface;
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/src/Token/GuardTokenInterface.php:
--------------------------------------------------------------------------------
1 |
13 | */
14 | interface GuardTokenInterface
15 | {
16 | }
17 |
--------------------------------------------------------------------------------
/src/Token/PostAuthenticationGuardToken.php:
--------------------------------------------------------------------------------
1 |
16 | */
17 | class PostAuthenticationGuardToken extends AbstractToken implements GuardTokenInterface
18 | {
19 | private $providerKey;
20 |
21 | /**
22 | * @param UserInterface $user The user!
23 | * @param string $providerKey The provider (firewall) key
24 | * @param RoleInterface[]|string[] $roles An array of roles
25 | *
26 | * @throws \InvalidArgumentException
27 | */
28 | public function __construct(UserInterface $user, $providerKey, array $roles)
29 | {
30 | parent::__construct($roles);
31 |
32 | if (empty($providerKey)) {
33 | throw new \InvalidArgumentException('$providerKey (i.e. firewall key) must not be empty.');
34 | }
35 |
36 | $this->setUser($user);
37 | $this->providerKey = $providerKey;
38 |
39 | // this token is meant to be used after authentication success, so it is always authenticated
40 | // you could set it as non authenticated later if you need to
41 | parent::setAuthenticated(true);
42 | }
43 |
44 | /**
45 | * {@inheritdoc}
46 | */
47 | public function setAuthenticated($isAuthenticated)
48 | {
49 | if ($isAuthenticated) {
50 | throw new \LogicException('Cannot set this token to trusted after instantiation.');
51 | }
52 |
53 | parent::setAuthenticated(false);
54 | }
55 |
56 | /**
57 | * This is meant to be only an authenticated token, where credentials
58 | * have already been used and are thus cleared.
59 | *
60 | * {@inheritdoc}
61 | */
62 | public function getCredentials()
63 | {
64 | return array();
65 | }
66 |
67 | /**
68 | * Returns the provider (firewall) key.
69 | *
70 | * @return string
71 | */
72 | public function getProviderKey()
73 | {
74 | return $this->providerKey;
75 | }
76 |
77 | /**
78 | * {@inheritdoc}
79 | */
80 | public function serialize()
81 | {
82 | return serialize(array($this->providerKey, parent::serialize()));
83 | }
84 |
85 | /**
86 | * {@inheritdoc}
87 | */
88 | public function unserialize($serialized)
89 | {
90 | list($this->providerKey, $parentStr) = unserialize($serialized);
91 | parent::unserialize($parentStr);
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/src/Token/PreAuthenticationGuardToken.php:
--------------------------------------------------------------------------------
1 |
15 | */
16 | class PreAuthenticationGuardToken extends AbstractToken implements GuardTokenInterface
17 | {
18 | private $credentials;
19 | private $guardProviderKey;
20 |
21 | /**
22 | * @param mixed $credentials
23 | * @param string $guardProviderKey Unique key that bind this token to a specific GuardAuthenticatorInterface
24 | */
25 | public function __construct($credentials, $guardProviderKey)
26 | {
27 | $this->credentials = $credentials;
28 | $this->guardProviderKey = $guardProviderKey;
29 |
30 | parent::__construct(array());
31 |
32 | // never authenticated
33 | parent::setAuthenticated(false);
34 | }
35 |
36 | public function getGuardProviderKey()
37 | {
38 | return $this->guardProviderKey;
39 | }
40 |
41 | /**
42 | * Returns the user credentials, which might be an array of anything you
43 | * wanted to put in there (e.g. username, password, favoriteColor).
44 | *
45 | * @return mixed The user credentials
46 | */
47 | public function getCredentials()
48 | {
49 | return $this->credentials;
50 | }
51 |
52 | public function setAuthenticated($authenticated)
53 | {
54 | throw new \LogicException('The PreAuthenticationGuardToken is *always* not authenticated.');
55 | }
56 | }
57 |
--------------------------------------------------------------------------------