1+ <?php
2+
3+ namespace Symfony \Component \Security \Guard \Firewall ;
4+
5+ use Symfony \Component \HttpFoundation \Request ;
6+ use Symfony \Component \HttpFoundation \Response ;
7+ use Symfony \Component \HttpKernel \Event \GetResponseEvent ;
8+ use Symfony \Component \Security \Guard \GuardAuthenticatorHandler ;
9+ use Symfony \Component \Security \Guard \Token \NonAuthenticatedGuardToken ;
10+ use Symfony \Component \Security \Core \Authentication \AuthenticationManagerInterface ;
11+ use Symfony \Component \Security \Guard \GuardAuthenticatorInterface ;
12+ use Psr \Log \LoggerInterface ;
13+ use Symfony \Component \Security \Core \Authentication \Token \TokenInterface ;
14+ use Symfony \Component \Security \Core \Exception \AuthenticationException ;
15+ use Symfony \Component \Security \Http \Firewall \ListenerInterface ;
16+ use Symfony \Component \Security \Http \RememberMe \RememberMeServicesInterface ;
17+
18+ /**
19+ * Authentication listener for the "guard" system
20+ *
21+ * @author Ryan Weaver <weaverryan@gmail.com>
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 , $ 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 getCredentialsFromRequest 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 ->getCredentialsFromRequest ($ 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 NonAuthenticatedGuardToken ($ 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+ }
0 commit comments