1 <?php
declare(strict_types
=1);
4 * This file is part of the Rapsys UserBundle package.
6 * (c) Raphaël Gertz <symfony@rapsys.eu>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Rapsys\UserBundle\Handler
;
14 use Symfony\Component\HttpFoundation\RedirectResponse
;
15 use Symfony\Component\HttpFoundation\Request
;
16 use Symfony\Component\HttpFoundation\Response
;
17 use Symfony\Component\Routing\Exception\InvalidParameterException
;
18 use Symfony\Component\Routing\Exception\MissingMandatoryParametersException
;
19 use Symfony\Component\Routing\Exception\ResourceNotFoundException
;
20 use Symfony\Component\Routing\Exception\RouteNotFoundException
;
21 use Symfony\Component\Routing\RequestContext
;
22 use Symfony\Component\Routing\RouterInterface
;
23 use Symfony\Component\Security\Core\Authentication\Token\TokenInterface
;
24 use Symfony\Component\Security\Http\Authentication\DefaultAuthenticationSuccessHandler
;
25 use Symfony\Component\Security\Http\ParameterBagUtils
;
26 use Symfony\Component\Security\Http\Util\TargetPathTrait
;
31 class AuthenticationSuccessHandler
extends DefaultAuthenticationSuccessHandler
{
33 * Allows to use getTargetPath and removeTargetPath private functions
40 protected $defaultOptions = [
41 'always_use_default_target_path' => false,
42 'default_target_path' => '/',
43 'login_path' => '/login',
44 'target_path_parameter' => '_target_path',
45 'use_referer' => false,
61 public function __construct(RouterInterface
$router, array $options = []) {
63 $this->router
= $router;
66 $this->setOptions($options);
70 * This is called when an interactive authentication attempt succeeds
72 * In use_referer case it will handle correctly when login_path is a route name or path
76 public function onAuthenticationSuccess(Request
$request, TokenInterface
$token): Response
{
78 $login = $request->get('_route');
80 //With login path option
81 if (!empty($loginPath = $this->options
['login_path'])) {
83 if ($loginPath[0] == '/') {
84 //Create login path request instance
85 $req = Request
::create($loginPath);
87 //Get login path pathinfo
88 $path = $req->getPathInfo();
91 $path = str_replace($request->getScriptName(), '', $path);
93 //Try with login path path
96 $oldContext = $this->router
->getContext();
99 //XXX: prevent MethodNotAllowedException on GET only routes because our context method is POST
100 //XXX: see vendor/symfony/routing/Matcher/Dumper/CompiledUrlMatcherTrait.php +42
101 $this->router
->setContext(new RequestContext());
103 //Retrieve route matching path
104 $route = $this->router
->match($path);
107 $this->router
->setContext($oldContext);
113 if (!empty($route['_route'])) {
115 $login = $route['_route'];
118 } catch (ResourceNotFoundException
$e) {
119 throw new \
UnexpectedValueException(sprintf('The "login_path" path "%s" must match a route', $this->options
['login_path']), $e->getCode(), $e);
123 //Try with login path route
125 //Retrieve route matching path
126 $path = $this->router
->generate($loginPath);
131 } catch (RouteNotFoundException
$e) {
132 throw new \
UnexpectedValueException(sprintf('The "login_path" route "%s" must match a route name', $this->options
['login_path']), $e->getCode(), $e);
133 //Ignore missing or invalid parameter
134 //XXX: useless or would not work ?
135 } catch (MissingMandatoryParametersException
|InvalidParameterException
$e) {
142 //Without always_use_default_target_path
143 if (empty($this->options
['always_use_default_target_path'])) {
145 if ($targetUrl = ParameterBagUtils
::getRequestParameterValue($request, $this->options
['target_path_parameter'])) {
149 //Return redirect to url response
150 return new RedirectResponse($url, 302);
151 //With session and target path in session
153 !empty($this->providerKey
) &&
154 ($session = $request->getSession()) &&
155 ($targetUrl = $this->getTargetPath($session, $this->providerKey
))
157 //Remove session target path
158 $this->removeTargetPath($session, $this->providerKey
);
163 //Return redirect to url response
164 return new RedirectResponse($url, 302);
165 //Extract and process referer
166 } elseif ($this->options
['use_referer'] && ($targetUrl = $request->headers
->get('referer'))) {
167 //Create referer request instance
168 $req = Request
::create($targetUrl);
171 $path = $req->getPathInfo();
173 //Get referer query string
174 $query = $req->getQueryString();
177 $path = str_replace($request->getScriptName(), '', $path);
179 //Try with referer path
182 $oldContext = $this->router
->getContext();
184 //Force clean context
185 //XXX: prevent MethodNotAllowedException on GET only routes because our context method is POST
186 //XXX: see vendor/symfony/routing/Matcher/Dumper/CompiledUrlMatcherTrait.php +42
187 $this->router
->setContext(new RequestContext());
189 //Retrieve route matching path
190 $route = $this->router
->match($path);
193 $this->router
->setContext($oldContext);
198 //With differing route from login one
199 if (($name = $route['_route']) != $login) {
200 //Remove route and controller from route defaults
201 unset($route['_route'], $route['_controller'], $route['_canonical_route']);
203 //Set url to generated one from referer route
204 $url = $this->router
->generate($name, $route);
206 //Return redirect to url response
207 return new RedirectResponse($url, 302);
210 } catch (ResourceNotFoundException
$e) {
211 //Unset target url, route and name
212 unset($targetUrl, $route, $name);
217 //With default target path option
218 if (!empty($defaultPath = $this->options
['default_target_path'])) {
220 if ($defaultPath[0] == '/') {
221 //Create login path request instance
222 $req = Request
::create($defaultPath);
224 //Get login path pathinfo
225 $path = $req->getPathInfo();
228 $path = str_replace($request->getScriptName(), '', $path);
230 //Try with login path path
233 $oldContext = $this->router
->getContext();
235 //Force clean context
236 //XXX: prevent MethodNotAllowedException on GET only routes because our context method is POST
237 //XXX: see vendor/symfony/routing/Matcher/Dumper/CompiledUrlMatcherTrait.php +42
238 $this->router
->setContext(new RequestContext());
240 //Retrieve route matching path
241 $route = $this->router
->match($path);
244 $this->router
->setContext($oldContext);
249 //Without login route name
250 if (($name = $route['_route']) != $login) {
251 //Remove route and controller from route defaults
252 unset($route['_route'], $route['_controller'], $route['_canonical_route']);
255 $url = $this->router
->generate($name, $route);
257 //Return redirect to url response
258 return new RedirectResponse($url, 302);
259 //With logout route name
261 //Unset default path, name and route
262 unset($defaultPath, $name, $route);
265 } catch (ResourceNotFoundException
$e) {
266 throw \
Exception('', $e->getCode(), $e);
267 //Unset default path, name and route
268 unset($defaultPath, $name, $route);
270 //Without login route name
271 } elseif ($defaultPath != $login) {
272 //Try with login path route
274 //Retrieve route matching path
275 $url = $this->router
->generate($defaultPath);
277 //Return redirect to url response
278 return new RedirectResponse($url, 302);
279 //Route not found, missing parameter or invalid parameter
280 } catch (RouteNotFoundException
|MissingMandatoryParametersException
|InvalidParameterException
$e) {
281 //Unset default path and url
282 unset($defaultPath, $url);
288 throw new \
UnexpectedValueException('You must provide a valid login target url or route name');