Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
58.06% covered (warning)
58.06%
18 / 31
25.00% covered (danger)
25.00%
1 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
CsrfTokenContextBuilder
58.06% covered (warning)
58.06%
18 / 31
25.00% covered (danger)
25.00%
1 / 4
31.59
0.00% covered (danger)
0.00%
0 / 1
 getCsrfToken
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
2.06
 process
87.50% covered (warning)
87.50%
14 / 16
0.00% covered (danger)
0.00%
0 / 1
8.12
 createToken
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 validateToken
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
20
1<?php
2namespace Apie\LaravelApie\ContextBuilders;
3
4use Apie\Core\Context\ApieContext;
5use Apie\Core\ContextBuilders\ContextBuilderInterface;
6use Apie\Core\ContextConstants;
7use Apie\Core\Exceptions\InvalidCsrfTokenException;
8use Apie\Core\Session\CsrfTokenProvider;
9use Apie\Core\Session\FakeTokenProvider;
10use Apie\Serializer\Encoders\FormSubmitDecoder;
11use Apie\Serializer\Interfaces\DecoderInterface;
12
13class CsrfTokenContextBuilder implements ContextBuilderInterface, CsrfTokenProvider
14{
15    private string $tokenName = 'apie,apie';
16
17    /** @var array<string, bool> */
18    private array $alreadyChecked = [];
19
20    private function getCsrfToken(): ?string
21    {
22        $session = app('session');
23
24        if (isset($session)) {
25            return $session->token();
26        }
27        return null;
28    }
29
30    public function process(ApieContext $context): ApieContext
31    {
32        $this->tokenName = $context->hasContext(ContextConstants::RESOURCE_NAME)
33            ? $context->getContext(ContextConstants::RESOURCE_NAME)
34            : 'apie';
35        $this->tokenName .= ',';
36        $this->tokenName .=  $context->hasContext(ContextConstants::APIE_ACTION)
37            ? $context->getContext(ContextConstants::APIE_ACTION)
38            : 'apie';
39        if (!app()->environment('testing')
40            && $context->hasContext(DecoderInterface::class)
41            && $context->hasContext(ContextConstants::RAW_CONTENTS)
42            && $context->getContext(DecoderInterface::class) instanceof FormSubmitDecoder) {
43            $csrf = $context->getContext(ContextConstants::RAW_CONTENTS)['_csrf'] ?? '';
44            $this->validateToken($csrf);
45        }
46
47
48        if (!$this->getCsrfToken()) {
49            return $context->withContext(CsrfTokenProvider::class, new FakeTokenProvider());
50        }
51        
52        return $context->withContext(CsrfTokenProvider::class, $this);
53    }
54
55    public function createToken(): string
56    {
57        return csrf_token();
58    }
59
60    public function validateToken(string $token): void
61    {
62        if (!empty($this->alreadyChecked[$token])) {
63            return;
64        }
65        $csrfToken = $this->getCsrfToken();
66        if (null === $csrfToken) {
67            $fakeProvider = new FakeTokenProvider;
68            $fakeProvider->validateToken($token);
69            return;
70        }
71        if (!hash_equals($csrfToken, $token)) {
72            throw new InvalidCsrfTokenException();
73        }
74        $this->alreadyChecked[$token] = true;
75    }
76}