Passed
Push — master ( 3eae54...10e872 )
by Greg
05:58
created

module.php$0 ➔ ipInCidr()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 5
c 1
b 0
f 0
nc 1
nop 2
dl 0
loc 9
rs 10
1
<?php
2
3
namespace MyCustomNamespace;
4
5
use Fisharebest\Webtrees\Module\AbstractModule;
6
use Fisharebest\Webtrees\Module\ModuleCustomInterface;
7
use Fisharebest\Webtrees\Module\ModuleCustomTrait;
8
use Psr\Http\Message\ResponseInterface;
9
use Psr\Http\Message\ServerRequestInterface;
10
use Psr\Http\Server\MiddlewareInterface;
11
use Psr\Http\Server\RequestHandlerInterface;
12
use function preg_match;
13
use function response;
14
15
/**
16
 * An example module to demonstrate middleware.
17
 */
18
return new class extends AbstractModule implements ModuleCustomInterface, MiddlewareInterface
19
{
20
    use ModuleCustomTrait;
21
22
    // Regular-expressions to match unwanted bots.
23
    private const BAD_USER_AGENTS = [
24
        '/AhrefsBot/',
25
        '/MJ12bot/',
26
        '/SeznamBot/',
27
    ];
28
29
    // List of unwanted IP ranges in CIDR format, e.g. "123.45.67.89/24".
30
    private const BAD_IP_RANGES = [
31
        '127.0.0.1/32',
32
    ];
33
34
    /**
35
     * How should this module be identified in the control panel, etc.?
36
     *
37
     * @return string
38
     */
39
    public function title(): string
40
    {
41
        return 'My custom middleware';
42
    }
43
44
    /**
45
     * A sentence describing what this module does.
46
     *
47
     * @return string
48
     */
49
    public function description(): string
50
    {
51
        return 'This is an example of middleware';
52
    }
53
54
    /**
55
     * Code here is executed before and after we process the request/response.
56
     * We can block access by throwing an exception.
57
     *
58
     * @param ServerRequestInterface  $request
59
     * @param RequestHandlerInterface $handler
60
     *
61
     * @return ResponseInterface
62
     */
63
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
64
    {
65
        // Code here is executed before we process the request/response.
66
67
        $ip_address = $request->getAttribute('client_ip');
68
        foreach (self::BAD_IP_RANGES as $bad_cidr) {
69
            if ($this->ipInCidr($ip_address, $bad_cidr)) {
70
                return response('IP address is not allowed: ' . $bad_cidr, 403);
71
            }
72
        }
73
74
        $user_agent = $request->getHeaderLine('HTTP_USER_AGENT');
75
        foreach (self::BAD_USER_AGENTS as $bad_user_agent) {
76
            if (preg_match($bad_user_agent, $user_agent)) {
77
                return response('User agent is not allowed: ' . $bad_user_agent, 403);
78
            }
79
        }
80
81
        // Generate the response.
82
        $response = $handler->handle($request);
83
84
        // Code here is executed after we process the request/response.
85
        // We can also modify the response.
86
        $response = $response->withHeader('X-Powered-By', 'Fish');
87
88
        return $response;
89
    }
90
91
    /**
92
     * Is an IP address in a CIDR range>
93
     *
94
     * @param string $ip
95
     * @param string $cidr
96
     *
97
     * @return bool
98
     */
99
    private function ipInCidr(string $ip, string $cidr): bool
100
    {
101
        [$net, $mask] = explode('/', $cidr);
102
103
        $ip_net  = ip2long($net);
104
        $ip_mask = ~((1 << (32 - $mask)) - 1);
105
        $ip_ip   = ip2long($ip);
106
107
        return ($ip_ip & $ip_mask) === ($ip_net & $ip_mask);
108
    }
109
};
110