Completed
Push — master ( 644be9...6ccaa2 )
by Dominik
02:10
created

CsrfMiddleware::__invoke()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 16
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 16
rs 9.2
c 0
b 0
f 0
cc 4
eloc 8
nc 8
nop 3
1
<?php
2
3
namespace Chubbyphp\Csrf;
4
5
use Chubbyphp\ErrorHandler\HttpException;
6
use Chubbyphp\Session\SessionInterface;
7
use Psr\Http\Message\ServerRequestInterface as Request;
8
use Psr\Http\Message\ResponseInterface as Response;
9
10
final class CsrfMiddleware
11
{
12
    /**
13
     * @var CsrfTokenGeneratorInterface
14
     */
15
    private $csrfTokenGenerator;
16
17
    /**
18
     * @var SessionInterface
19
     */
20
    private $session;
21
22
    const CSRF_KEY = 'csrf';
23
24
    const EXCEPTION_STATUS = 424;
25
26
    const EXCEPTION_MISSING_IN_SESSION = 'Csrf token is missing within session';
27
    const EXCEPTION_MISSING_IN_BODY = 'Csrf token is missing within body';
28
    const EXCEPTION_IS_NOT_SAME = 'Csrf token within body is not the same as in session';
29
30
    /**
31
     * @param CsrfTokenGeneratorInterface $csrfTokenGenerator
32
     * @param SessionInterface            $session
33
     */
34
    public function __construct(CsrfTokenGeneratorInterface $csrfTokenGenerator, SessionInterface $session)
35
    {
36
        $this->csrfTokenGenerator = $csrfTokenGenerator;
37
        $this->session = $session;
38
    }
39
40
    /**
41
     * @param Request  $request
42
     * @param Response $response
43
     * @param callable $next
44
     *
45
     * @return Response
46
     */
47
    public function __invoke(Request $request, Response $response, callable $next = null)
48
    {
49
        if (in_array($request->getMethod(), ['POST', 'PUT', 'DELETE', 'PATCH'])) {
50
            $this->checkCsrf($request, $response);
51
        }
52
53
        if (!$this->session->has($request, self::CSRF_KEY)) {
54
            $this->session->set($request, self::CSRF_KEY, $this->csrfTokenGenerator->generate());
55
        }
56
57
        if (null !== $next) {
58
            $response = $next($request, $response);
59
        }
60
61
        return $response;
62
    }
63
64
    /**
65
     * @param Request  $request
66
     * @param Response $response
67
     *
68
     * @throws HttpException
69
     */
70
    private function checkCsrf(Request $request, Response $response)
71
    {
72
        if (!$this->session->has($request, self::CSRF_KEY)) {
73
            $this->throwException($request, $response, self::EXCEPTION_MISSING_IN_SESSION);
74
        }
75
76
        $data = $request->getParsedBody();
77
78
        if (!isset($data[self::CSRF_KEY])) {
79
            $this->throwException($request, $response, self::EXCEPTION_MISSING_IN_BODY);
80
        }
81
82
        if ($this->session->get($request, self::CSRF_KEY) !== $data[self::CSRF_KEY]) {
83
            $this->throwException($request, $response, self::EXCEPTION_IS_NOT_SAME);
84
        }
85
    }
86
87
    /**
88
     * @param Request  $request
89
     * @param Response $response
90
     * @param string   $message
91
     *
92
     * @throws HttpException
93
     */
94
    private function throwException(Request $request, Response $response, string $message)
95
    {
96
        throw HttpException::create($request, $response, self::EXCEPTION_STATUS, $message);
97
    }
98
}
99