Completed
Push — master ( 2a724b...f106a8 )
by Alexander
12:30 queued 10:35
created

Csrf::getToken()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 5
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 9
ccs 6
cts 6
cp 1
crap 2
rs 10
1
<?php
2
3
namespace Yiisoft\Yii\Web\Middleware;
4
5
use Psr\Http\Message\ResponseFactoryInterface;
6
use Psr\Http\Message\ResponseInterface;
7
use Psr\Http\Message\ServerRequestInterface;
8
use Psr\Http\Server\MiddlewareInterface;
9
use Psr\Http\Server\RequestHandlerInterface;
10
use Yiisoft\Router\Method;
11
use Yiisoft\Security\Random;
12
use Yiisoft\Security\TokenMasker;
13
use Yiisoft\Yii\Web\Session\SessionInterface;
14
15
final class Csrf implements MiddlewareInterface
16
{
17
    private const NAME = '_csrf';
18
    public const HEADER_NAME = 'X-CSRF-Token';
19
    public const REQUEST_NAME = 'csrf_token';
20
21
    private $name = self::NAME;
22
    private $requestName = self::REQUEST_NAME;
23
    private $responseFactory;
24
    private $session;
25
26 8
    public function __construct(ResponseFactoryInterface $responseFactory, SessionInterface $session)
27
    {
28 8
        $this->responseFactory = $responseFactory;
29 8
        $this->session = $session;
30
    }
31
32 8
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
33
    {
34 8
        $token = $this->getToken();
35
36 8
        if (!$this->validateCsrfToken($request, $token)) {
37 3
            $this->session->remove($this->name);
38
39 3
            $response = $this->responseFactory->createResponse(400);
40 3
            $response->getBody()->write('Unable to verify your data submission.');
41 3
            return $response;
42
        }
43
44 5
        $request = $request->withAttribute($this->requestName, TokenMasker::mask($token));
45
46 5
        return $handler->handle($request);
47
    }
48
49 8
    public function setName(string $name): void
50
    {
51 8
        $this->name = $name;
52
    }
53
54
    public function setRequestName(string $name): void
55
    {
56
        $this->requestName = $name;
57
    }
58
59 8
    private function getToken(): ?string
60
    {
61 8
        $token = $this->session->get($this->name);
62 8
        if (empty($token)) {
63 2
            $token = Random::string();
64 2
            $this->session->set($this->name, $token);
65
        }
66
67 8
        return $token;
68
    }
69
70 8
    private function validateCsrfToken(ServerRequestInterface $request, ?string $trueToken): bool
71
    {
72 8
        $method = $request->getMethod();
73
74 8
        if (\in_array($method, [Method::GET, Method::HEAD, Method::OPTIONS], true)) {
75 1
            return true;
76
        }
77
78 7
        $unmaskedToken = $this->getTokenFromRequest($request);
79
80 7
        return !empty($unmaskedToken) && hash_equals($unmaskedToken, $trueToken);
81
    }
82
83 7
    private function getTokenFromRequest(ServerRequestInterface $request): ?string
84
    {
85 7
        $parsedBody = $request->getParsedBody();
86
87 7
        $token = $parsedBody[$this->name] ?? null;
88 7
        if (empty($token)) {
89 2
            $headers = $request->getHeader(self::HEADER_NAME);
90 2
            $token = \reset($headers);
91
        }
92
93 7
        return TokenMasker::unmask($token);
94
    }
95
}
96