Completed
Push — master ( ac1fa0...9301b0 )
by Neomerx
02:58
created

CsrfMiddleware::handle()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 20
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 20
ccs 10
cts 10
cp 1
rs 9.2
c 0
b 0
f 0
cc 4
eloc 14
nc 3
nop 3
crap 4
1
<?php namespace Limoncello\Application\Packages\Csrf;
2
3
/**
4
 * Copyright 2015-2018 [email protected]
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 * http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
18
19
use Closure;
20
use Limoncello\Application\Contracts\Csrf\CsrfTokenStorageInterface;
21
use Limoncello\Contracts\Application\MiddlewareInterface;
22
use Limoncello\Contracts\Settings\SettingsProviderInterface;
23
use Psr\Container\ContainerInterface;
24
use Psr\Http\Message\ResponseInterface;
25
use Psr\Http\Message\ServerRequestInterface;
26
use Zend\Diactoros\Response\EmptyResponse;
27
28
/**
29
 * @package Limoncello\Application
30
 */
31
class CsrfMiddleware implements MiddlewareInterface
32
{
33
    /** Middleware handler */
34
    const CALLABLE_HANDLER = [self::class, self::MIDDLEWARE_METHOD_NAME];
35
36
    /** @var string Default error response factory on invalid/absent CSRF token */
37
    const DEFAULT_ERROR_RESPONSE_METHOD = 'defaultErrorResponse';
38
39
    /**
40
     * @inheritdoc
41
     */
42 2
    public static function handle(
43
        ServerRequestInterface $request,
44
        Closure $next,
45
        ContainerInterface $container
46
    ): ResponseInterface {
47 2
        $settings = static::getCsrfSettings($container);
0 ignored issues
show
Bug introduced by
Since getCsrfSettings() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of getCsrfSettings() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
48 2
        $methods  = $settings[CsrfSettings::INTERNAL_HTTP_METHODS_TO_CHECK_AS_UC_KEYS];
49
50 2
        if (array_key_exists(strtoupper($request->getMethod()), $methods) === true) {
51 2
            $token = static::readToken($request, $settings[CsrfSettings::HTTP_REQUEST_CSRF_TOKEN_KEY]);
0 ignored issues
show
Bug introduced by
Since readToken() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of readToken() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
52 2
            if (is_string($token) === false || static::getTokenStorage($container)->check($token) === false) {
0 ignored issues
show
Bug introduced by
Since getTokenStorage() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of getTokenStorage() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
53 1
                $errResponseMethod = $settings[CsrfSettings::CREATE_ERROR_RESPONSE_METHOD];
54 1
                $errResponse       = call_user_func($errResponseMethod, $container, $request);
55
56 1
                return $errResponse;
57
            }
58
        }
59
60 1
        return $next($request);
61
    }
62
63
    /**
64
     * @param ContainerInterface     $container
65
     * @param ServerRequestInterface $request
66
     *
67
     * @return ResponseInterface
68
     */
69 1
    public static function defaultErrorResponse(
70
        ContainerInterface $container,
71
        ServerRequestInterface $request
72
    ): ResponseInterface {
73 1
        assert($container !== null && $request !== null);
74
75
        // forbid if no valid CSRF token
76 1
        return new EmptyResponse(403);
77
    }
78
79
    /**
80
     * @param ServerRequestInterface $request
81
     * @param string                 $tokenKey
82
     *
83
     * @return null|string
84
     */
85 2
    private static function readToken(ServerRequestInterface $request, string $tokenKey): ?string
86
    {
87 2
        $token = is_array($form = $request->getParsedBody()) === true ? ($form[$tokenKey] ?? null) : null;
88
89 2
        return $token;
90
    }
91
92
    /**
93
     * @param ContainerInterface $container
94
     *
95
     * @return CsrfTokenStorageInterface
96
     */
97 2
    private static function getTokenStorage(ContainerInterface $container): CsrfTokenStorageInterface
98
    {
99 2
        assert($container->has(CsrfTokenStorageInterface::class) === true);
100
        /** @var CsrfTokenStorageInterface $csrfStorage */
101 2
        $csrfStorage = $container->get(CsrfTokenStorageInterface::class);
102
103 2
        return $csrfStorage;
104
    }
105
106
    /**
107
     * @param ContainerInterface $container
108
     *
109
     * @return array
110
     */
111 2
    private static function getCsrfSettings(ContainerInterface $container): array
112
    {
113
        /** @var SettingsProviderInterface $provider */
114 2
        assert($container->has(SettingsProviderInterface::class) === true);
115 2
        $provider = $container->get(SettingsProviderInterface::class);
116
117 2
        assert($provider->has(CsrfSettings::class));
118 2
        $settings = $provider->get(CsrfSettings::class);
119
120 2
        return $settings;
121
    }
122
}
123