Passed
Push — master ( a2929b...1a0f41 )
by Gabor
02:32
created

CSRFMiddleware::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * WebHemi.
4
 *
5
 * PHP version 7.1
6
 *
7
 * @copyright 2012 - 2018 Gixx-web (http://www.gixx-web.com)
8
 * @license   https://opensource.org/licenses/MIT The MIT License (MIT)
9
 *
10
 * @link http://www.gixx-web.com
11
 */
12
declare(strict_types = 1);
13
14
namespace WebHemi\Middleware\Security;
15
16
use Generator;
17
use RecursiveArrayIterator;
18
use RecursiveIteratorIterator;
19
use RuntimeException;
20
use WebHemi\CSRF\ServiceInterface as CSRFInterface;
21
use WebHemi\Http\ResponseInterface;
22
use WebHemi\Http\ServerRequestInterface;
23
use WebHemi\Middleware\MiddlewareInterface;
24
25
/**
26
 * Class CSRFMiddleware.
27
 */
28
class CSRFMiddleware implements MiddlewareInterface
29
{
30
    /**
31
     * @var CSRFInterface
32
     */
33
    private $csrfAdapter;
34
35
    /**
36
     * AclMiddleware constructor.
37
     *
38
     * @param CSRFInterface $csrfAdapter
39
     */
40
    public function __construct(CSRFInterface $csrfAdapter)
41
    {
42
        $this->csrfAdapter = $csrfAdapter;
43
    }
44
45
    /**
46
     * A middleware is a callable. It can do whatever is appropriate with the Request and Response objects.
47
     * The only hard requirement is that a middleware MUST return an instance of \Psr\Http\Message\ResponseInterface.
48
     * Each middleware SHOULD invoke the next middleware and pass it Request and Response objects as arguments.
49
     *
50
     * @param  ServerRequestInterface $request
51
     * @param  ResponseInterface      $response
52
     * @throws RuntimeException
53
     * @return void
54
     */
55
    public function __invoke(ServerRequestInterface&$request, ResponseInterface&$response) : void
56
    {
57
        // Get requests will not harm
58
        if ($request->getMethod() == 'GET') {
59
            return;
60
        }
61
62
        $csrfToken = '';
63
64
        foreach ($this->recursiveFind($request->getParsedBody(), CSRFInterface::SESSION_KEY) as $value) {
0 ignored issues
show
Bug introduced by
It seems like $request->getParsedBody() can also be of type null and object; however, parameter $haystack of WebHemi\Middleware\Secur...leware::recursiveFind() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

64
        foreach ($this->recursiveFind(/** @scrutinizer ignore-type */ $request->getParsedBody(), CSRFInterface::SESSION_KEY) as $value) {
Loading history...
65
            $csrfToken = $value;
66
            break;
67
        }
68
69
        if (empty($csrfToken)) {
70
            throw new RuntimeException('CSRF token is missing from the request', 405);
71
        }
72
73
        $tokenTTL = $request->isXmlHttpRequest() ? null : CSRFInterface::SESSION_TTL_IN_SECONDS;
74
        $allowMultipleUse = $request->isXmlHttpRequest();
75
        $result = $this->csrfAdapter->verify($csrfToken, $tokenTTL, $allowMultipleUse);
76
77
        if (!$result) {
78
            throw new RuntimeException('The provided CSRF token is not valid or outdated.', 403);
79
        }
80
    }
81
82
    /**
83
     * @param array $haystack
84
     * @param $needle
85
     * @return Generator
86
     */
87
    protected function recursiveFind(array $haystack, $needle)
88
    {
89
        $iterator  = new RecursiveArrayIterator($haystack);
90
        $recursive = new RecursiveIteratorIterator(
91
            $iterator,
92
            RecursiveIteratorIterator::SELF_FIRST
93
        );
94
        foreach ($recursive as $key => $value) {
95
            if ($key === $needle) {
96
                yield $value;
97
            }
98
        }
99
    }
100
}
101