Passed
Branch master (3daac1)
by Vincent
07:53
created

CsrfElement::root()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
cc 2
nc 2
nop 0
crap 2
1
<?php
2
3
namespace Bdf\Form\Csrf;
4
5
use BadMethodCallException;
6
use Bdf\Form\Child\Http\HttpFieldPath;
7
use Bdf\Form\ElementInterface;
8
use Bdf\Form\Error\FormError;
9
use Bdf\Form\Leaf\LeafRootElement;
10
use Bdf\Form\Leaf\View\SimpleElementView;
11
use Bdf\Form\RootElementInterface;
12
use Bdf\Form\Util\ContainerTrait;
13
use Bdf\Form\View\ElementViewInterface;
14
use Symfony\Component\Security\Csrf\CsrfToken;
15
use Symfony\Component\Security\Csrf\CsrfTokenManager;
16
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
17
18
/**
19
 * Element for add a CSRF token on the form
20
 * The token value is auto-generated using CsrfTokenManagerInterface
21
 *
22
 * This element is always required, and validated, and cannot be `import()'d`
23
 *
24
 * The dependency "symfony/security-csrf" is required to use this element
25
 *
26
 * @see CsrfTokenManager
27
 */
28
final class CsrfElement implements ElementInterface
29
{
30
    use ContainerTrait;
31
32
    /**
33
     * @var string
34
     */
35
    private $tokenId;
36
37
    /**
38
     * @var CsrfValueValidator
39
     */
40
    private $validator;
41
42
    /**
43
     * @var CsrfTokenManagerInterface
44
     */
45
    private $tokenManager;
46
47
    /**
48
     * @var CsrfToken|null
49
     */
50
    private $value = null;
51
52
    /**
53
     * @var FormError
54
     */
55
    private $error;
56
57
    /**
58
     * CsrfElement constructor.
59
     *
60
     * @param string|null $tokenId
61
     * @param CsrfValueValidator|null $validator
62
     * @param CsrfTokenManagerInterface|null $tokenManager
63
     */
64 20
    public function __construct(?string $tokenId = null, ?CsrfValueValidator $validator = null, ?CsrfTokenManagerInterface $tokenManager = null)
65
    {
66 20
        $this->tokenId = $tokenId ?: self::class;
67 20
        $this->validator = $validator ?: new CsrfValueValidator();
68 20
        $this->tokenManager = $tokenManager ?: new CsrfTokenManager();
69
70 20
        $this->error = FormError::null();
71 20
    }
72
73
    /**
74
     * {@inheritdoc}
75
     */
76 11
    public function submit($data): ElementInterface
77
    {
78 11
        $this->value = new CsrfToken($this->tokenId, $data);
79 11
        $this->error = $this->validator->validate($this->value, $this);
80
81 11
        return $this;
82
    }
83
84
    /**
85
     * {@inheritdoc}
86
     */
87
    public function patch($data): ElementInterface
88
    {
89
        // CSRF element must be submitted
90
        return $this->submit($data);
91
    }
92
93
    /**
94
     * {@inheritdoc}
95
     */
96 1
    public function import($entity): ElementInterface
97
    {
98 1
        throw new BadMethodCallException('Cannot set a Csrf token value');
99
    }
100
101
    /**
102
     * {@inheritdoc}
103
     */
104 13
    public function value(): CsrfToken
105
    {
106 13
        if ($this->value) {
107 8
            return $this->value;
108
        }
109
110 11
        return $this->value = $this->tokenManager->getToken($this->tokenId);
111
    }
112
113
    /**
114
     * {@inheritdoc}
115
     */
116 5
    public function httpValue(): string
117
    {
118 5
        return $this->value()->getValue();
119
    }
120
121
    /**
122
     * {@inheritdoc}
123
     */
124 9
    public function valid(): bool
125
    {
126 9
        return $this->value && $this->error->empty();
127
    }
128
129
    /**
130
     * {@inheritdoc}
131
     */
132 8
    public function error(): FormError
133
    {
134 8
        return $this->error;
135
    }
136
137
    /**
138
     * {@inheritdoc}
139
     */
140 13
    public function root(): RootElementInterface
141
    {
142 13
        return $this->container() ? $this->container()->parent()->root() : new LeafRootElement($this);
143
    }
144
145
    /**
146
     * {@inheritdoc}
147
     */
148 1
    public function view(?HttpFieldPath $field = null): ElementViewInterface
149
    {
150 1
        return new SimpleElementView(
151 1
            self::class,
152 1
            (string) $field,
153 1
            $this->tokenManager->getToken($this->tokenId), // Always get the real token value
154 1
            $this->error->global(),
155 1
            true, // Token is always required
156 1
            []
157
        );
158
    }
159
160
    /**
161
     * @return CsrfTokenManagerInterface
162
     * @internal Used by the validator
163
     */
164 12
    public function getTokenManager(): CsrfTokenManagerInterface
165
    {
166 12
        return $this->tokenManager;
167
    }
168
169
    /**
170
     * Invalidate the current CSRF token
171
     * After this call, the CSRF cannot be valid anymore
172
     */
173 2
    public function invalidateToken(): void
174
    {
175 2
        $this->tokenManager->removeToken($this->tokenId);
176 2
    }
177
}
178