Completed
Push — master ( eeebc2...21cf4a )
by Matze
03:31
created

Csrf::processRequest()   B

Complexity

Conditions 7
Paths 4

Size

Total Lines 19
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 7

Importance

Changes 5
Bugs 0 Features 1
Metric Value
c 5
b 0
f 1
dl 0
loc 19
ccs 10
cts 10
cp 1
rs 8.2222
cc 7
eloc 11
nc 4
nop 2
crap 7
1
<?php
2
3
namespace BrainExe\Core\Middleware;
4
5
use BrainExe\Core\Annotations\Middleware;
6
use BrainExe\Core\Traits\IdGeneratorTrait;
7
use BrainExe\Core\Traits\TimeTrait;
8
use Symfony\Component\HttpFoundation\Cookie;
9
use Symfony\Component\HttpFoundation\Request;
10
use Symfony\Component\HttpFoundation\Response;
11
use Symfony\Component\HttpFoundation\Session\SessionInterface;
12
use Symfony\Component\Routing\Exception\MethodNotAllowedException;
13
use Symfony\Component\Routing\Route;
14
15
/**
16
 * @Middleware("Middleware.Csrf")
17
 */
18
class Csrf extends AbstractMiddleware
19
{
20
21
    const CSRF   = 'csrf';
22
    const HEADER = 'X-XSRF-TOKEN';
23
    const COOKIE = 'XSRF-TOKEN';
24
25
    const LIFETIME = 3600; // 1h
26
27
    use IdGeneratorTrait;
28
    use TimeTrait;
29
30
    /**
31
     * @var string
32
     */
33
    private $newToken = null;
34
35
    /**
36
     * {@inheritdoc}
37 3
     */
38
    public function processRequest(Request $request, Route $route)
39 3
    {
40
        $givenToken = $request->headers->get(self::HEADER);
41 3
42 1
        $session = $request->getSession();
43 1
        $expectedToken = $session->get(self::CSRF);
44
        if ($request->isMethod('GET') && (!$route->hasOption(self::CSRF)) || $route->hasDefault('_guest')) {
45 1
            if (empty($expectedToken)) {
46
                $this->renewCsrfToken();
47
            }
48 2
            return;
49 2
        }
50
51 2
        if (empty($givenToken) || $givenToken !== $expectedToken) {
52 1
            throw new MethodNotAllowedException(['POST'], 'invalid CSRF token');
53
        }
54
55
        $this->generateNewTokenWhenNeeded($session);
56 1
    }
57 1
58 1
    /**
59 1
     * {@inheritdoc}
60
     */
61 1
    public function processResponse(Request $request, Response $response)
62
    {
63
        if ($this->newToken) {
64
            $session = $request->getSession();
65
            $session->set(self::CSRF, $this->newToken);
66 2
            $session->set('csrf_timestamp', $this->now());
67
            $response->headers->setCookie(new Cookie(self::COOKIE, $this->newToken, 0, '/', null, false, false));
68 2
            $this->newToken = null;
69 2
        }
70 2
    }
71 2
72 2
    /**
73 2
     * @return void
74
     */
75 2
    private function renewCsrfToken()
76
    {
77
        $this->newToken = $this->generateRandomId();
78
    }
79
80 2
    /**
81
     * @param SessionInterface $session
82 2
     */
83 2
    private function generateNewTokenWhenNeeded(SessionInterface $session)
84
    {
85
        $now        = $this->now();
86
        $lastUpdate = $session->get('csrf_timestamp');
87
        if ($lastUpdate + self::LIFETIME < $now) {
88
            $this->renewCsrfToken();
89
        }
90
    }
91
}
92