Completed
Pull Request — 5.6 (#2830)
by Jeroen
14:14
created

AdminBundle/EventListener/ToolbarListener.php (3 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace Kunstmaan\AdminBundle\EventListener;
4
5
use Kunstmaan\AdminBundle\Helper\AdminRouteHelper;
6
use Kunstmaan\AdminBundle\Helper\Toolbar\DataCollector;
7
use Symfony\Component\DependencyInjection\ContainerInterface;
8
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
9
use Symfony\Component\HttpFoundation\Request;
10
use Symfony\Component\HttpFoundation\Response;
11
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
12
use Symfony\Component\HttpKernel\Event\ResponseEvent;
13
use Symfony\Component\HttpKernel\HttpKernel;
14
use Symfony\Component\HttpKernel\KernelEvents;
15
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
16
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
17
use Symfony\Component\Security\Core\Authorization\AuthorizationChecker;
18
use Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken;
19
use Twig\Environment;
20
21
class ToolbarListener implements EventSubscriberInterface
22
{
23
    const DISABLED = 1;
24
25
    const ENABLED = 2;
26
27
    /**
28
     * @var Environment
29
     */
30
    protected $twig;
31
32
    /**
33
     * @var UrlGeneratorInterface
34
     */
35
    protected $urlGenerator;
36
37
    /**
38
     * @var DataCollector
39
     */
40
    protected $dataCollector;
41
42
    /**
43
     * @var AuthorizationChecker
44
     */
45
    protected $authorizationChecker;
46
47
    /**
48
     * @var TokenStorageInterface
49
     */
50
    protected $tokenStorage;
51
52
    /**
53
     * @var bool
54
     */
55
    protected $enabled;
56
57
    /**
58
     * @var ContainerInterface
59
     */
60
    private $container;
61
62
    /**
63
     * @var AdminRouteHelper
64
     */
65
    protected $adminRouteHelper;
66
67
    /**
68
     * @var array
69
     */
70
    protected $providerKeys;
71
72
    /**
73
     * @var array
74
     */
75
    protected $adminFirewallName;
76
77
    /**
78
     * ToolbarListener constructor.
79
     *
80
     * @param bool   $enabled
81
     * @param string $adminFirewallName
82
     */
83
    public function __construct(
84
        Environment $twig,
85
        UrlGeneratorInterface $urlGenerator,
86
        DataCollector $dataCollector,
87
        AuthorizationChecker $authorizationChecker,
88
        TokenStorageInterface $tokenStorage,
89
        $enabled,
90
        ContainerInterface $container,
91
        AdminRouteHelper $adminRouteHelper,
92
        array $providerKeys,
93
        $adminFirewallName = 'main'
94
    ) {
95
        $this->twig = $twig;
96
        $this->urlGenerator = $urlGenerator;
97
        $this->dataCollector = $dataCollector;
98
        $this->authorizationChecker = $authorizationChecker;
99
        $this->tokenStorage = $tokenStorage;
100
        $this->enabled = $enabled;
101
        $this->container = $container;
102
        $this->adminRouteHelper = $adminRouteHelper;
103
        $this->providerKeys = $providerKeys;
104
        $this->adminFirewallName = $adminFirewallName;
0 ignored issues
show
Documentation Bug introduced by
It seems like $adminFirewallName of type string is incompatible with the declared type array of property $adminFirewallName.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
105
    }
106
107
    /**
108
     * @return array
0 ignored issues
show
Consider making the return type a bit more specific; maybe use array<*,array<string|integer>>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
109
     */
110 3
    public static function getSubscribedEvents()
111
    {
112
        return [
113 3
            KernelEvents::RESPONSE => ['onKernelResponse', -125],
114
        ];
115
    }
116
117
    /**
118
     * @return bool
119
     */
120
    public function isEnabled()
121
    {
122
        return !$this->container->has('profiler') && $this->enabled;
123
    }
124
125
    /**
126
     * @param FilterResponseEvent|ResponseEvent $event
127
     */
128
    public function onKernelResponse($event)
129
    {
130 View Code Duplication
        if (!$event instanceof FilterResponseEvent && !$event instanceof ResponseEvent) {
131
            throw new \InvalidArgumentException(\sprintf('Expected instance of type %s, %s given', \class_exists(ResponseEvent::class) ? ResponseEvent::class : FilterResponseEvent::class, \is_object($event) ? \get_class($event) : \gettype($event)));
132
        }
133
134
        if (!$this->isEnabled() || HttpKernel::MASTER_REQUEST !== $event->getRequestType()) {
135
            return;
136
        }
137
138
        $response = $event->getResponse();
139
        $request = $event->getRequest();
140
        $session = $request->getSession();
141
        $url = $event->getRequest()->getRequestUri();
142
        $token = $this->tokenStorage->getToken();
143
144
        if (null !== $token && method_exists($token, 'getProviderKey')) {
145
            $key = $token->getProviderKey();
146
        } else {
147
            $key = $this->adminFirewallName;
148
        }
149
150
        // Only enable toolbar when the kunstmaan_admin.toolbar_firewall_names config value contains the current firewall name.
151
        if (!\in_array($key, $this->providerKeys, false)) {
152
            return false;
153
        }
154
155
        // Only enable toolbar when we can find an authenticated user in the session from the kunstmaan_admin.admin_firewall_name config value.
156
        $authenticated = false;
157
        /* @var PostAuthenticationGuardToken $token */
158
        if ($session->isStarted() && $session->has(sprintf('_security_%s', $this->adminFirewallName))) {
159
            $token = unserialize($session->get(sprintf('_security_%s', $this->adminFirewallName)));
160
            $authenticated = $token->isAuthenticated();
161
        }
162
163
        // Do not capture redirects or modify XML HTTP Requests
164
        if (!$authenticated || !$event->isMasterRequest() || $request->isXmlHttpRequest() || $this->adminRouteHelper->isAdminRoute($url)) {
165
            return;
166
        }
167
168 View Code Duplication
        if ($response->isRedirection() || ($response->headers->has('Content-Type') && false === strpos(
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
169
                    $response->headers->get('Content-Type'),
170
                    'html'
171
                ))
172
            || 'html' !== $request->getRequestFormat()
173
            || false !== stripos($response->headers->get('Content-Disposition'), 'attachment;')
174
        ) {
175
            return;
176
        }
177
178
        $this->injectToolbar($response, $request);
179
    }
180
181
    /**
182
     * Injects the admin toolbar into the given Response.
183
     *
184
     * @param Response $response A Response instance
185
     */
186
    protected function injectToolbar(Response $response, Request $request)
187
    {
188
        $content = $response->getContent();
189
        $pos = strripos($content, '</body>');
190
191
        if (false !== $pos) {
192
            $toolbar = "\n" . str_replace(
193
                    "\n",
194
                    '',
195
                    $this->twig->render(
196
                        '@KunstmaanAdmin/Toolbar/toolbar.html.twig',
197
                        ['collectors' => $this->dataCollector->getDataCollectors()]
198
                    )
199
                ) . "\n";
200
            $content = substr($content, 0, $pos) . $toolbar . substr($content, $pos);
201
            $response->setContent($content);
202
        }
203
    }
204
}
205