Passed
Pull Request — master (#157)
by
unknown
02:27
created

php$0   A

Complexity

Total Complexity 2

Size/Duplication

Total Lines 16
Duplicated Lines 0 %

Importance

Changes 7
Bugs 1 Features 0
Metric Value
c 7
b 1
f 0
dl 0
loc 16
rs 10
wmc 2
1
<?php
2
3
namespace Yiisoft\Yii\Web\Tests\Middleware;
4
5
use Nyholm\Psr7\Factory\Psr17Factory;
6
use Nyholm\Psr7\ServerRequest;
7
use PHPUnit\Framework\TestCase;
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 Yiisoft\Yii\Web\Middleware\TrustedHostsNetworkResolver;
13
use Yiisoft\Yii\Web\Tests\Middleware\Mock\MockRequestHandler;
14
15
class TrustedHostsNetworkResolverTest extends TestCase
16
{
17
    protected function newRequestWithSchemaAndHeaders(
18
        string $scheme = 'http',
19
        array $headers = [],
20
        array $serverParams = []
21
    ): ServerRequestInterface {
22
        $request = new ServerRequest('GET', '/', $headers, null, '1.1', $serverParams);
23
        $uri = $request->getUri()->withScheme($scheme);
24
        return $request->withUri($uri);
25
    }
26
27
    public function trustedDataProvider(): array
28
    {
29
        return [
30
            'xForwardLevel1' => [
31
                ['x-forwarded-for' => ['9.9.9.9', '5.5.5.5', '2.2.2.2']],
32
                ['REMOTE_ADDR' => '127.0.0.1'],
33
                [
34
                    ['hosts' => ['8.8.8.8', '127.0.0.1'], 'ipHeaders' => ['x-forwarded-for']]
35
                ],
36
                '2.2.2.2',
37
            ],
38
            'xForwardLevel2' => [
39
                ['x-forwarded-for' => ['9.9.9.9', '5.5.5.5', '2.2.2.2']],
40
                ['REMOTE_ADDR' => '127.0.0.1'],
41
                [
42
                    ['hosts' => ['8.8.8.8', '127.0.0.1', '2.2.2.2'], 'ipHeaders' => ['x-forwarded-for']],
43
                ],
44
                '5.5.5.5',
45
            ],
46
            'forwardLevel1' => [
47
                ['forward' => ['for=9.9.9.9', 'for=5.5.5.5', 'for=2.2.2.2']],
48
                ['REMOTE_ADDR' => '127.0.0.1'],
49
                [
50
                    [
51
                        'hosts' => ['8.8.8.8', '127.0.0.1'],
52
                        'ipHeaders' => [[TrustedHostsNetworkResolver::IP_HEADER_TYPE_RFC7239, 'forward']],
53
                    ]
54
                ],
55
                '2.2.2.2',
56
            ],
57
            'forwardLevel2' => [
58
                ['forward' => ['for=9.9.9.9', 'for=5.5.5.5', 'for=2.2.2.2']],
59
                ['REMOTE_ADDR' => '127.0.0.1'],
60
                [
61
                    [
62
                        'hosts' => ['8.8.8.8', '127.0.0.1', '2.2.2.2'],
63
                        'ipHeaders' => [[TrustedHostsNetworkResolver::IP_HEADER_TYPE_RFC7239, 'forward']],
64
                    ],
65
66
                ],
67
                '5.5.5.5',
68
            ],
69
            'forwardLevel2HostAndProtocol' => [
70
                ['forward' => ['for=9.9.9.9', 'proto=https;for=5.5.5.5;host=test', 'for=2.2.2.2']],
71
                ['REMOTE_ADDR' => '127.0.0.1'],
72
                [
73
                    [
74
                        'hosts' => ['8.8.8.8', '127.0.0.1', '2.2.2.2'],
75
                        'ipHeaders' => [[TrustedHostsNetworkResolver::IP_HEADER_TYPE_RFC7239, 'forward']],
76
                    ],
77
                ],
78
                '5.5.5.5',
79
                'test',
80
                'https',
81
            ],
82
            'forwardLevel2HostAndProtocolAndUrl' => [
83
                [
84
                    'forward' => ['for=9.9.9.9', 'proto=https;for=5.5.5.5;host=test', 'for=2.2.2.2'],
85
                    'x-rewrite-url' => ['/test?test=test'],
86
                ],
87
                ['REMOTE_ADDR' => '127.0.0.1'],
88
                [
89
                    [
90
                        'hosts' => ['8.8.8.8', '127.0.0.1', '2.2.2.2'],
91
                        'ipHeaders' => [[TrustedHostsNetworkResolver::IP_HEADER_TYPE_RFC7239, 'forward']],
92
                        'urlHeaders' => ['x-rewrite-url'],
93
                    ],
94
                ],
95
                '5.5.5.5',
96
                'test',
97
                'https',
98
                '/test',
99
                'test=test',
100
            ],
101
        ];
102
    }
103
104
    /**
105
     * @dataProvider trustedDataProvider
106
     */
107
    public function testTrusted(
108
        array $headers,
109
        array $serverParams,
110
        array $trustedHosts,
111
        string $expectedClientIp,
112
        ?string $expectedHttpHost = null,
113
        string $expectedHttpScheme = 'http',
114
        string $expectedPath = '/',
115
        string $expectedQuery = ''
116
    ): void {
117
        $request = $this->newRequestWithSchemaAndHeaders('http', $headers, $serverParams);
118
        $requestHandler = new MockRequestHandler();
119
120
        $middleware = new TrustedHostsNetworkResolver(new Psr17Factory());
121
        foreach ($trustedHosts as $data) {
122
            $middleware = $middleware->withAddedTrustedHosts(
123
                $data['hosts'],
124
                $data['ipHeaders'] ?? [],
125
                $data['protocolHeaders'] ?? [],
126
                $data['hostHeaders'] ?? [],
127
                $data['urlHeaders'] ?? [],
128
                $data['trustedHeaders'] ?? null);
129
        }
130
        $response = $middleware->process($request, $requestHandler);
131
        $this->assertSame(200, $response->getStatusCode());
132
        $this->assertSame($expectedClientIp, $requestHandler->processedRequest->getAttribute('requestClientIp'));
133
        if ($expectedHttpHost !== null) {
134
            $this->assertSame($expectedHttpHost, $requestHandler->processedRequest->getUri()->getHost());
135
        }
136
        $this->assertSame($expectedHttpScheme, $requestHandler->processedRequest->getUri()->getScheme());
137
        $this->assertSame($expectedPath, $requestHandler->processedRequest->getUri()->getPath());
138
        $this->assertSame($expectedQuery, $requestHandler->processedRequest->getUri()->getQuery());
139
    }
140
141
    public function notTrustedDataProvider(): array
142
    {
143
        return [
144
            'none' => [
145
                [],
146
                ['REMOTE_ADDR' => '127.0.0.1'],
147
                [],
148
            ],
149
            'x-forwarded-for' => [
150
                ['x-forwarded-for' => ['9.9.9.9', '5.5.5.5', '2.2.2.2']],
151
                ['REMOTE_ADDR' => '127.0.0.1'],
152
                [['hosts' => ['8.8.8.8'], 'ipHeaders' => ['x-forwarded-for']]],
153
            ],
154
            'forward' => [
155
                ['x-forwarded-for' => ['for=9.9.9.9', 'for=5.5.5.5', 'for=2.2.2.2']],
156
                ['REMOTE_ADDR' => '127.0.0.1'],
157
                [['hosts' => ['8.8.8.8'], 'ipHeaders' => ['x-forwarded-for']]],
158
            ],
159
        ];
160
    }
161
162
    /**
163
     * @dataProvider notTrustedDataProvider
164
     */
165
    public function testNotTrusted(array $headers, array $serverParams, array $trustedHosts): void
166
    {
167
        $request = $this->newRequestWithSchemaAndHeaders('http', $headers, $serverParams);
168
        $requestHandler = new MockRequestHandler();
169
170
        $middleware = new TrustedHostsNetworkResolver(new Psr17Factory());
171
        foreach ($trustedHosts as $data) {
172
            $middleware = $middleware->withAddedTrustedHosts(
173
                $data['hosts'],
174
                $data['ipHeaders'] ?? [],
175
                $data['protocolHeaders'] ?? [],
176
                [],
177
                [],
178
                $data['trustedHeaders'] ?? []);
179
        }
180
        $response = $middleware->process($request, $requestHandler);
181
        $this->assertSame(412, $response->getStatusCode());
182
    }
183
184
    public function testNotTrustedMiddleware(): void
185
    {
186
        $request = $this->newRequestWithSchemaAndHeaders('http', [], [
187
            'REMOTE_ADDR' => '127.0.0.1',
188
        ]);
189
        $requestHandler = new MockRequestHandler();
190
191
        $middleware = new TrustedHostsNetworkResolver(new Psr17Factory());
192
        $content = 'Another branch.';
193
        $middleware = $middleware->withNotTrustedBranch(new class($content) implements MiddlewareInterface
194
        {
195
            private $content;
196
197
            public function __construct(string $content)
198
            {
199
                $this->content = $content;
200
            }
201
202
            public function process(
203
                ServerRequestInterface $request,
204
                RequestHandlerInterface $handler
205
            ): ResponseInterface {
206
                $response = (new Psr17Factory())->createResponse(403);
207
                $response->getBody()->write($this->content);
208
                return $response;
209
            }
210
        });
211
        $response = $middleware->process($request, $requestHandler);
212
        $this->assertInstanceOf(ResponseInterface::class, $response);
213
        $this->assertSame(403, $response->getStatusCode());
214
        $body = $response->getBody();
215
        $body->rewind();
216
        $this->assertSame($content, $body->getContents());
217
    }
218
219
    public function addedTrustedHostsInvalidParameterDataProvider(): array
220
    {
221
        return [
222
            'hostsEmpty' => ['hosts' => []],
223
            'hostsEmptyString' => ['hosts' => ['']],
224
            'hostsNumeric' => ['hosts' => [888]],
225
            'hostsSpaces' => ['hosts' => ['    ']],
226
            'hostsNotDomain' => ['host' => ['-apple']],
227
            'urlHeadersEmpty' => ['urlHeaders' => ['']],
228
            'urlHeadersNumeric' => ['urlHeaders' => [888]],
229
            'urlHeadersSpaces' => ['urlHeaders' => ['   ']],
230
            'trustedHeadersEmpty' => ['trustedHeaders' => ['']],
231
            'trustedHeadersNumeric' => ['trustedHeaders' => [888]],
232
            'trustedHeadersSpaces' => ['trustedHeaders' => ['   ']],
233
            'protocolHeadersNumeric' => ['protocolHeaders' => ['http' => 888]],
234
            'ipHeadersEmptyString' => ['ipHeaders' => [' ']],
235
            'ipHeadersNumeric' => ['ipHeaders' => [888]],
236
            'ipHeadersInvalidType' => ['ipHeaders' => [['---', 'aaa']]],
237
            'ipHeadersInvalidTypeValue' => [
238
                'ipHeaders' => [
239
                    [
240
                        TrustedHostsNetworkResolver::IP_HEADER_TYPE_RFC7239,
241
                        888
242
                    ]
243
                ]
244
            ],
245
        ];
246
    }
247
248
    /**
249
     * @dataProvider addedTrustedHostsInvalidParameterDataProvider
250
     */
251
    public function testAddedTrustedHostsInvalidParameter(array $data): void
252
    {
253
        $this->expectException(\InvalidArgumentException::class);
254
        (new TrustedHostsNetworkResolver(new Psr17Factory()))
255
            ->withAddedTrustedHosts($data['hosts'] ?? [],
256
                $data['ipHeaders'] ?? [],
257
                $data['protocolHeaders'] ?? [],
258
                $data['hostHeaders'] ?? [],
259
                $data['urlHeaders'] ?? [],
260
                $data['trustedHeaders'] ?? null
261
            );
262
    }
263
}
264