]> Raphaël G. Git Repositories - userbundle/blob - Listener/LogoutListener.php
Add note about normalisation prevention
[userbundle] / Listener / LogoutListener.php
1 <?php declare(strict_types=1);
2
3 /*
4 * This file is part of the Rapsys UserBundle package.
5 *
6 * (c) Raphaël Gertz <symfony@rapsys.eu>
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 Rapsys\UserBundle\Listener;
13
14 use Symfony\Component\DependencyInjection\ContainerInterface;
15 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
16 use Symfony\Component\HttpFoundation\RedirectResponse;
17 use Symfony\Component\HttpFoundation\Request;
18 use Symfony\Component\Routing\Exception\InvalidParameterException;
19 use Symfony\Component\Routing\Exception\MissingMandatoryParametersException;
20 use Symfony\Component\Routing\Exception\ResourceNotFoundException;
21 use Symfony\Component\Routing\Exception\RouteNotFoundException;
22 use Symfony\Component\Routing\RequestContext;
23 use Symfony\Component\Routing\RouterInterface;
24 use Symfony\Component\Security\Http\Event\LogoutEvent;
25
26 use Rapsys\UserBundle\RapsysUserBundle;
27
28 /**
29 * {@inheritdoc}
30 */
31 class LogoutListener implements EventSubscriberInterface {
32 /**
33 * Config array
34 */
35 protected $config;
36
37 /**
38 * Target url
39 */
40 private $targetUrl;
41
42 /**
43 * {@inheritdoc}
44 *
45 * @xxx Second argument will be replaced by security.firewalls.main.logout.target
46 * @see vendor/symfony/security-bundle/Resources/config/security_listeners.php +79
47 */
48 public function __construct(ContainerInterface $container, string $targetUrl, RouterInterface $router) {
49 //Set config
50 $this->config = $container->getParameter(RapsysUserBundle::getAlias());
51
52 //Set target url
53 $this->targetUrl = $targetUrl;
54
55 //Set router
56 $this->router = $router;
57 }
58
59 /**
60 * {@inheritdoc}
61 */
62 public function onLogout(LogoutEvent $event): void {
63 //Get request
64 $request = $event->getRequest();
65
66 //Retrieve logout route
67 $logout = $request->attributes->get('_route');
68
69 //Extract and process referer
70 if (($referer = $request->headers->get('referer'))) {
71 //Create referer request instance
72 $req = Request::create($referer);
73
74 //Get referer path
75 $path = $req->getPathInfo();
76
77 //Get referer query string
78 $query = $req->getQueryString();
79
80 //Remove script name
81 $path = str_replace($request->getScriptName(), '', $path);
82
83 //Try with referer path
84 try {
85 //Save old context
86 $oldContext = $this->router->getContext();
87
88 //Force clean context
89 //XXX: prevent MethodNotAllowedException on GET only routes because our context method is POST
90 //XXX: see vendor/symfony/routing/Matcher/Dumper/CompiledUrlMatcherTrait.php +42
91 $this->router->setContext(new RequestContext());
92
93 //Retrieve route matching path
94 $route = $this->router->match($path);
95
96 //Reset context
97 $this->router->setContext($oldContext);
98
99 //Clear old context
100 unset($oldContext);
101
102 //Without logout route name
103 if (($name = $route['_route']) != $logout) {
104 //Remove route and controller from route defaults
105 unset($route['_route'], $route['_controller'], $route['_canonical_route']);
106
107 //Generate url
108 $url = $this->router->generate($name, $route);
109
110 //Set event response
111 $event->setResponse(new RedirectResponse($url, 302));
112
113 //Return
114 return;
115 //With logout route name
116 } else {
117 //Unset referer and route
118 unset($referer, $route);
119 }
120 //No route matched
121 } catch (ResourceNotFoundException $e) {
122 //Unset referer and route
123 unset($referer, $route);
124 }
125 }
126
127 //With index route from config
128 if (!empty($name = $this->config['route']['index']['name']) && is_array($context = $this->config['route']['index']['context'])) {
129 //Without logout route name
130 if ($name != $logout) {
131 //Try index route
132 try {
133 //Generate url
134 $url = $this->router->generate($name, $context);
135
136 //Set event response
137 $event->setResponse(new RedirectResponse($url, 302));
138
139 //Return
140 return;
141 //No route matched
142 } catch (ResourceNotFoundException $e) {
143 //Unset name and context
144 unset($name, $context);
145 }
146 //With logout route name
147 } else {
148 //Unset name and context
149 unset($name, $context);
150 }
151 }
152
153 //Try target url
154 try {
155 //Save old context
156 $oldContext = $this->router->getContext();
157
158 //Force clean context
159 //XXX: prevent MethodNotAllowedException on GET only routes because our context method is POST
160 //XXX: see vendor/symfony/routing/Matcher/Dumper/CompiledUrlMatcherTrait.php +42
161 $this->router->setContext(new RequestContext());
162
163 //With logout target path
164 if ($this->targetUrl[0] == '/') {
165 //Retrieve route matching target url
166 $route = $this->router->match($this->targetUrl);
167
168 //Reset context
169 $this->router->setContext($oldContext);
170
171 //Clear old context
172 unset($oldContext);
173
174 //Without logout route name
175 if (($name = $route['_route']) != $logout) {
176 //Remove route and controller from route defaults
177 unset($route['_route'], $route['_controller'], $route['_canonical_route']);
178
179 //Generate url
180 $url = $this->router->generate($name, $route);
181
182 //Set event response
183 $event->setResponse(new RedirectResponse($url, 302));
184
185 //Return
186 return;
187 //With logout route name
188 } else {
189 //Unset name and route
190 unset($name, $route);
191 }
192 //With route name
193 } else {
194 //Retrieve route matching path
195 $url = $this->router->generate($this->targetUrl);
196
197 //Set event response
198 $event->setResponse(new RedirectResponse($url, 302));
199
200 //Return
201 return;
202 }
203 //Get first route from route collection if / path was not matched
204 } catch (ResourceNotFoundException|RouteNotFoundException|MissingMandatoryParametersException|InvalidParameterException $e) {
205 //Unset name and route
206 unset($name, $route);
207 }
208
209 //Set event response
210 $event->setResponse(new RedirectResponse('/', 302));
211 }
212
213 /**
214 * {@inheritdoc}
215 */
216 public static function getSubscribedEvents(): array {
217 return [
218 LogoutEvent::class => ['onLogout', 64],
219 ];
220 }
221 }