Completed
Push — EZP-26146-location-swap-urlali... ( 334d77...8d4853 )
by
unknown
63:56 queued 37:49
created

CsrfListener::isSessionRoute()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 7
rs 9.4285
1
<?php
2
3
/**
4
 * File containing the CsrfListener class.
5
 *
6
 * @copyright Copyright (C) eZ Systems AS. All rights reserved.
7
 * @license For full copyright and license information view LICENSE file distributed with this source code.
8
 *
9
 * @version //autogentag//
10
 */
11
namespace eZ\Bundle\EzPublishRestBundle\EventListener;
12
13
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
14
use Symfony\Component\HttpFoundation\Request;
15
use Symfony\Component\HttpKernel\KernelEvents;
16
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
17
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
18
use eZ\Publish\Core\Base\Exceptions\UnauthorizedException;
19
use eZ\Bundle\EzPublishRestBundle\RestEvents;
20
use Symfony\Component\Security\Csrf\CsrfToken;
21
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
22
23
class CsrfListener implements EventSubscriberInterface
24
{
25
    /**
26
     * Name of the HTTP header containing CSRF token.
27
     */
28
    const CSRF_TOKEN_HEADER = 'X-CSRF-Token';
29
30
    /**
31
     * @var null|CsrfTokenManagerInterface
32
     */
33
    private $csrfTokenManager;
34
35
    /**
36
     * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
37
     */
38
    private $eventDispatcher;
39
40
    /**
41
     * @var bool
42
     */
43
    private $csrfEnabled;
44
45
    /**
46
     * @var bool
47
     */
48
    private $csrfTokenIntention;
49
50
    /**
51
     * Note that CSRF provider needs to be optional as it will not be available
52
     * when CSRF protection is disabled.
53
     *
54
     * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $eventDispatcher
55
     * @param bool $csrfEnabled
56
     * @param string $csrfTokenIntention
57
     * @param null|CsrfTokenManagerInterface $csrfTokenManager
58
     */
59
    public function __construct(
60
        EventDispatcherInterface $eventDispatcher,
61
        $csrfEnabled,
62
        $csrfTokenIntention,
63
        CsrfTokenManagerInterface $csrfTokenManager = null
64
    ) {
65
        $this->eventDispatcher = $eventDispatcher;
66
        $this->csrfEnabled = $csrfEnabled;
67
        $this->csrfTokenIntention = $csrfTokenIntention;
0 ignored issues
show
Documentation Bug introduced by
The property $csrfTokenIntention was declared of type boolean, but $csrfTokenIntention is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
68
        $this->csrfTokenManager = $csrfTokenManager;
69
    }
70
71
    /**
72
     * @return array
73
     */
74
    public static function getSubscribedEvents()
75
    {
76
        return array(
77
            KernelEvents::REQUEST => 'onKernelRequest',
78
        );
79
    }
80
81
    /**
82
     * This method validates CSRF token if CSRF protection is enabled.
83
     *
84
     * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
85
     *
86
     * @throws \eZ\Publish\Core\Base\Exceptions\UnauthorizedException
87
     */
88
    public function onKernelRequest(GetResponseEvent $event)
89
    {
90
        if (!$event->getRequest()->attributes->get('is_rest_request')) {
91
            return;
92
        }
93
94
        if (!$this->csrfEnabled) {
95
            return;
96
        }
97
98
        // skip CSRF validation if no session is running
99
        if (!$event->getRequest()->getSession()->isStarted()) {
100
            return;
101
        }
102
103
        if ($this->isMethodSafe($event->getRequest()->getMethod())) {
104
            return;
105
        }
106
107
        if ($this->isSessionRoute($event->getRequest()->get('_route'))) {
108
            return;
109
        }
110
111
        if (!$this->checkCsrfToken($event->getRequest())) {
112
            throw new UnauthorizedException(
113
                'Missing or invalid CSRF token',
114
                $event->getRequest()->getMethod() . ' ' . $event->getRequest()->getPathInfo()
115
            );
116
        }
117
118
        // Dispatching event so that CSRF token intention can be injected into Legacy Stack
119
        $this->eventDispatcher->dispatch(RestEvents::REST_CSRF_TOKEN_VALIDATED);
120
    }
121
122
    /**
123
     * @param string $method
124
     *
125
     * @return bool
126
     */
127
    protected function isMethodSafe($method)
128
    {
129
        return in_array($method, array('GET', 'HEAD', 'OPTIONS'));
130
    }
131
132
    /**
133
     * @param string $route
134
     *
135
     * @return bool
136
     *
137
     * @deprecated Deprecated since 6.5. Use isSessionRoute() instead.
138
     */
139
    protected function isLoginRequest($route)
140
    {
141
        return $route === 'ezpublish_rest_createSession';
142
    }
143
144
    /**
145
     * Tests if a given $route is a session management one.
146
     *
147
     * @param string $route
148
     *
149
     * @return bool
150
     */
151
    protected function isSessionRoute($route)
152
    {
153
        return in_array(
154
            $route,
155
            ['ezpublish_rest_createSession', 'ezpublish_rest_refreshSession', 'ezpublish_rest_deleteSession']
156
        );
157
    }
158
159
    /**
160
     * Checks the validity of the request's csrf token header.
161
     *
162
     * @param Request $request
163
     *
164
     * @return bool true/false if the token is valid/invalid, false if none was found in the request's headers.
165
     */
166
    protected function checkCsrfToken(Request $request)
167
    {
168
        if (!$request->headers->has(self::CSRF_TOKEN_HEADER)) {
169
            return false;
170
        }
171
172
        return $this->csrfTokenManager->isTokenValid(
173
            new CsrfToken(
174
                $this->csrfTokenIntention,
0 ignored issues
show
Documentation introduced by
$this->csrfTokenIntention is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
175
                $request->headers->get(self::CSRF_TOKEN_HEADER)
176
            )
177
        );
178
    }
179
}
180