CsrfSettings   A
last analyzed

Complexity

Total Complexity 9

Size/Duplication

Total Lines 97
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 1

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 9
lcom 0
cbo 1
dl 0
loc 97
ccs 33
cts 33
cp 1
rs 10
c 0
b 0
f 0

2 Methods

Rating   Name   Duplication   Size   Complexity  
B get() 0 42 8
A getSettings() 0 14 1
1
<?php declare(strict_types=1);
2
3
namespace Limoncello\Application\Packages\Csrf;
4
5
/**
6
 * Copyright 2015-2020 [email protected]
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 * http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20
21
use Limoncello\Common\Reflection\CheckCallableTrait;
22
use Limoncello\Contracts\Settings\SettingsInterface;
23
use Psr\Container\ContainerInterface;
24
use Psr\Http\Message\ResponseInterface;
25
use Psr\Http\Message\ServerRequestInterface;
26
use ReflectionException;
27
use function assert;
28
use function is_int;
29
use function is_null;
30
use function is_string;
31
use function strtoupper;
32
33
/**
34
 * @package Limoncello\Application
35
 */
36
class CsrfSettings implements SettingsInterface
37
{
38
    use CheckCallableTrait;
39
40
    /** @var string Default form field for storing CSRF value */
41
    public const DEFAULT_HTTP_REQUEST_CSRF_TOKEN_KEY = '_token';
42
43
    /** @var int Settings key */
44
    public const HTTP_METHODS_TO_CHECK = 0;
45
46
    /** @var int Settings key */
47
    public const HTTP_REQUEST_CSRF_TOKEN_KEY = self::HTTP_METHODS_TO_CHECK + 1;
48
49
    /** @var int Settings key */
50
    public const TOKEN_STORAGE_KEY_IN_SESSION = self::HTTP_REQUEST_CSRF_TOKEN_KEY + 1;
51
52
    /** @var int Settings key */
53
    public const MAX_TOKENS = self::TOKEN_STORAGE_KEY_IN_SESSION + 1;
54
55
    /** @var int Settings key */
56
    public const MAX_TOKENS_THRESHOLD = self::MAX_TOKENS + 1;
57
58
    /** @var int Settings key */
59
    public const CREATE_ERROR_RESPONSE_METHOD = self::MAX_TOKENS_THRESHOLD + 1;
60
61
    /** @var int Settings key */
62
    public const INTERNAL_HTTP_METHODS_TO_CHECK_AS_UC_KEYS = self::CREATE_ERROR_RESPONSE_METHOD + 1;
63
64
    /** @var int Settings key */
65 5
    public const KEY_LAST = self::INTERNAL_HTTP_METHODS_TO_CHECK_AS_UC_KEYS + 1;
66
67 5
    /**
68
     * @inheritdoc
69
     *
70 5
     * @throws ReflectionException
71 5
     */
72 5
    final public function get(array $appConfig): array
73 5
    {
74 5
        $settings = $this->getSettings();
75 5
76
        // check and transform HTTP methods
77 5
        $methods = $settings[static::HTTP_METHODS_TO_CHECK] ?? [];
78 5
        assert(empty($methods) === false);
79
        $upperCaseMethods = [];
80
        foreach ($methods as $method) {
81 5
            assert(is_string($method) === true && empty($method) === false);
82 5
            $upperCaseMethods[strtoupper($method)] = true;
83
        }
84
        $settings[static::INTERNAL_HTTP_METHODS_TO_CHECK_AS_UC_KEYS] = $upperCaseMethods;
85 5
        unset($settings[static::HTTP_METHODS_TO_CHECK]);
86 5
87
        // check token key
88
        $tokenKey = $settings[static::HTTP_REQUEST_CSRF_TOKEN_KEY];
89 5
        assert(is_string($tokenKey) === true && empty($tokenKey) === false);
90 5
91
        // check storage key
92
        $storageKey = $settings[static::TOKEN_STORAGE_KEY_IN_SESSION];
93 5
        assert(is_string($storageKey) === true && empty($storageKey) === false);
94 5
95
        // check max tokens
96 5
        $maxTokens = $settings[static::MAX_TOKENS];
97 5
        assert(is_null($maxTokens) === true || (is_int($maxTokens) === true && $maxTokens > 0));
98 5
99 5
        // check max tokens
100 5
        $maxTokensThreshold = $settings[static::MAX_TOKENS];
101
        assert(is_int($maxTokensThreshold) === true && $maxTokensThreshold >= 0);
102 5
103
        $errorResponseMethod = $settings[static::CREATE_ERROR_RESPONSE_METHOD] ?? null;
104
        $expectedArgs        = [ContainerInterface::class, ServerRequestInterface::class];
105 5
        $expectedRet         = ResponseInterface::class;
106
        assert(
107
            $this->checkPublicStaticCallable($errorResponseMethod, $expectedArgs, $expectedRet) === true,
108
            'CSRF error response method should have signature ' .
109
            '(ContainerInterface, ServerRequestInterface): ResponseInterface.'
110
        );
111 5
112
        return $settings;
113
    }
114 5
115
    /**
116
     * @return array
117 5
     */
118 5
    protected function getSettings(): array
119 5
    {
120 5
        // defaults
121 5
        $errorResponseMethod = [CsrfMiddleware::class, CsrfMiddleware::DEFAULT_ERROR_RESPONSE_METHOD];
122 5
123
        return [
124
            static::HTTP_METHODS_TO_CHECK        => ['POST', 'PUT', 'DELETE', 'PATCH'],
125
            static::HTTP_REQUEST_CSRF_TOKEN_KEY  => static::DEFAULT_HTTP_REQUEST_CSRF_TOKEN_KEY,
126
            static::TOKEN_STORAGE_KEY_IN_SESSION => 'csrf_tokens',
127
            static::MAX_TOKENS                   => 20,
128
            static::MAX_TOKENS_THRESHOLD         => 5,
129
            static::CREATE_ERROR_RESPONSE_METHOD => $errorResponseMethod,
130
        ];
131
    }
132
}
133