Completed
Pull Request — master (#157)
by
unknown
02:08
created

newRequestWithSchemaAndHeaders()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 1
eloc 3
c 3
b 0
f 0
nc 1
nop 3
dl 0
loc 8
rs 10
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
            'rfc7239Level1' => [
47
                ['forwarded' => ['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, 'forwarded']],
53
                    ]
54
                ],
55
                '2.2.2.2',
56
            ],
57
            'rfc7239Level2' => [
58
                ['forwarded' => ['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, 'forwarded']],
64
                    ],
65
66
                ],
67
                '5.5.5.5',
68
            ],
69
            'rfc7239Level2HostAndProtocol' => [
70
                ['forwarded' => ['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, 'forwarded']],
76
                        'hostHeaders' => ['forwarded'],
77
                        'protocolHeaders' => ['forwarded' => ['http' => 'http', 'https' => 'https']],
78
                    ],
79
                ],
80
                '5.5.5.5',
81
                'test',
82
                'https',
83
            ],
84
            'rfc7239Level2HostAndProtocolAndUrl' => [
85
                [
86
                    'forwarded' => ['for=9.9.9.9', 'proto=https;for=5.5.5.5;host=test', 'for=2.2.2.2'],
87
                    'x-rewrite-url' => ['/test?test=test'],
88
                ],
89
                ['REMOTE_ADDR' => '127.0.0.1'],
90
                [
91
                    [
92
                        'hosts' => ['8.8.8.8', '127.0.0.1', '2.2.2.2'],
93
                        'ipHeaders' => [[TrustedHostsNetworkResolver::IP_HEADER_TYPE_RFC7239, 'forwarded']],
94
                        'hostHeaders' => ['forwarded'],
95
                        'protocolHeaders' => ['forwarded' => ['http' => 'http', 'https' => 'https']],
96
                        'urlHeaders' => ['x-rewrite-url'],
97
                    ],
98
                ],
99
                '5.5.5.5',
100
                'test',
101
                'https',
102
                '/test',
103
                'test=test',
104
            ],
105
            'rfc7239Level2AnotherHostAndAnotherProtocolAndUrl' => [
106
                [
107
                    'forwarded' => ['for=9.9.9.9', 'proto=https;for=5.5.5.5;host=test', 'for=2.2.2.2'],
108
                    'x-rewrite-url' => ['/test?test=test'],
109
                    'x-forwarded-host' => ['test.another'],
110
                    'x-forwarded-proto' => ['on']
111
                ],
112
                ['REMOTE_ADDR' => '127.0.0.1'],
113
                [
114
                    [
115
                        'hosts' => ['8.8.8.8', '127.0.0.1', '2.2.2.2'],
116
                        'ipHeaders' => [[TrustedHostsNetworkResolver::IP_HEADER_TYPE_RFC7239, 'forwarded']],
117
                        'hostHeaders' => ['x-forwarded-host', 'forwarded'],
118
                        'protocolHeaders' => [
119
                            'x-forwarded-proto' => ['http' => 'http', 'httpsss' => 'on'],
120
                            'forwarded' => ['http' => 'http', 'https' => 'https']
121
                        ],
122
                        'urlHeaders' => ['x-rewrite-url'],
123
                    ],
124
                ],
125
                '5.5.5.5',
126
                'test.another',
127
                'httpsss',
128
                '/test',
129
                'test=test',
130
            ],
131
        ];
132
    }
133
134
    /**
135
     * @dataProvider trustedDataProvider
136
     */
137
    public function testTrusted(
138
        array $headers,
139
        array $serverParams,
140
        array $trustedHosts,
141
        string $expectedClientIp,
142
        ?string $expectedHttpHost = null,
143
        string $expectedHttpScheme = 'http',
144
        string $expectedPath = '/',
145
        string $expectedQuery = ''
146
    ): void {
147
        $request = $this->newRequestWithSchemaAndHeaders('http', $headers, $serverParams);
148
        $requestHandler = new MockRequestHandler();
149
150
        $middleware = new TrustedHostsNetworkResolver(new Psr17Factory());
151
        foreach ($trustedHosts as $data) {
152
            $middleware = $middleware->withAddedTrustedHosts(
153
                $data['hosts'],
154
                $data['ipHeaders'] ?? [],
155
                $data['protocolHeaders'] ?? [],
156
                $data['hostHeaders'] ?? [],
157
                $data['urlHeaders'] ?? [],
158
                $data['trustedHeaders'] ?? null);
159
        }
160
        $response = $middleware->process($request, $requestHandler);
161
        $this->assertSame(200, $response->getStatusCode());
162
        $this->assertSame($expectedClientIp, $requestHandler->processedRequest->getAttribute('requestClientIp'));
163
        if ($expectedHttpHost !== null) {
164
            $this->assertSame($expectedHttpHost, $requestHandler->processedRequest->getUri()->getHost());
165
        }
166
        $this->assertSame($expectedHttpScheme, $requestHandler->processedRequest->getUri()->getScheme());
167
        $this->assertSame($expectedPath, $requestHandler->processedRequest->getUri()->getPath());
168
        $this->assertSame($expectedQuery, $requestHandler->processedRequest->getUri()->getQuery());
169
    }
170
171
    public function notTrustedDataProvider(): array
172
    {
173
        return [
174
            'none' => [
175
                [],
176
                ['REMOTE_ADDR' => '127.0.0.1'],
177
                [],
178
            ],
179
            'x-forwarded-for' => [
180
                ['x-forwarded-for' => ['9.9.9.9', '5.5.5.5', '2.2.2.2']],
181
                ['REMOTE_ADDR' => '127.0.0.1'],
182
                [['hosts' => ['8.8.8.8'], 'ipHeaders' => ['x-forwarded-for']]],
183
            ],
184
            'rfc7239' => [
185
                ['x-forwarded-for' => ['for=9.9.9.9', 'for=5.5.5.5', 'for=2.2.2.2']],
186
                ['REMOTE_ADDR' => '127.0.0.1'],
187
                [['hosts' => ['8.8.8.8'], 'ipHeaders' => ['x-forwarded-for']]],
188
            ],
189
        ];
190
    }
191
192
    /**
193
     * @dataProvider notTrustedDataProvider
194
     */
195
    public function testNotTrusted(array $headers, array $serverParams, array $trustedHosts): void
196
    {
197
        $request = $this->newRequestWithSchemaAndHeaders('http', $headers, $serverParams);
198
        $requestHandler = new MockRequestHandler();
199
200
        $middleware = new TrustedHostsNetworkResolver(new Psr17Factory());
201
        foreach ($trustedHosts as $data) {
202
            $middleware = $middleware->withAddedTrustedHosts(
203
                $data['hosts'],
204
                $data['ipHeaders'] ?? [],
205
                $data['protocolHeaders'] ?? [],
206
                [],
207
                [],
208
                $data['trustedHeaders'] ?? []);
209
        }
210
        $response = $middleware->process($request, $requestHandler);
211
        $this->assertSame(412, $response->getStatusCode());
212
    }
213
214
    public function testNotTrustedMiddleware(): void
215
    {
216
        $request = $this->newRequestWithSchemaAndHeaders('http', [], [
217
            'REMOTE_ADDR' => '127.0.0.1',
218
        ]);
219
        $requestHandler = new MockRequestHandler();
220
221
        $middleware = new TrustedHostsNetworkResolver(new Psr17Factory());
222
        $content = 'Another branch.';
223
        $middleware = $middleware->withNotTrustedBranch(new class($content) implements MiddlewareInterface
224
        {
225
            private $content;
226
227
            public function __construct(string $content)
228
            {
229
                $this->content = $content;
230
            }
231
232
            public function process(
233
                ServerRequestInterface $request,
234
                RequestHandlerInterface $handler
235
            ): ResponseInterface {
236
                $response = (new Psr17Factory())->createResponse(403);
237
                $response->getBody()->write($this->content);
238
                return $response;
239
            }
240
        });
241
        $response = $middleware->process($request, $requestHandler);
242
        $this->assertInstanceOf(ResponseInterface::class, $response);
243
        $this->assertSame(403, $response->getStatusCode());
244
        $body = $response->getBody();
245
        $body->rewind();
246
        $this->assertSame($content, $body->getContents());
247
    }
248
249
    public function addedTrustedHostsInvalidParameterDataProvider(): array
250
    {
251
        return [
252
            'hostsEmpty' => ['hosts' => []],
253
            'hostsEmptyString' => ['hosts' => ['']],
254
            'hostsNumeric' => ['hosts' => [888]],
255
            'hostsSpaces' => ['hosts' => ['    ']],
256
            'hostsNotDomain' => ['host' => ['-apple']],
257
            'urlHeadersEmpty' => ['urlHeaders' => ['']],
258
            'urlHeadersNumeric' => ['urlHeaders' => [888]],
259
            'urlHeadersSpaces' => ['urlHeaders' => ['   ']],
260
            'trustedHeadersEmpty' => ['trustedHeaders' => ['']],
261
            'trustedHeadersNumeric' => ['trustedHeaders' => [888]],
262
            'trustedHeadersSpaces' => ['trustedHeaders' => ['   ']],
263
            'protocolHeadersNumeric' => ['protocolHeaders' => ['http' => 888]],
264
            'ipHeadersEmptyString' => ['ipHeaders' => [' ']],
265
            'ipHeadersNumeric' => ['ipHeaders' => [888]],
266
            'ipHeadersInvalidType' => ['ipHeaders' => [['---', 'aaa']]],
267
            'ipHeadersInvalidTypeValue' => [
268
                'ipHeaders' => [
269
                    [
270
                        TrustedHostsNetworkResolver::IP_HEADER_TYPE_RFC7239,
271
                        888
272
                    ]
273
                ]
274
            ],
275
        ];
276
    }
277
278
    /**
279
     * @dataProvider addedTrustedHostsInvalidParameterDataProvider
280
     */
281
    public function testAddedTrustedHostsInvalidParameter(array $data): void
282
    {
283
        $this->expectException(\InvalidArgumentException::class);
284
        (new TrustedHostsNetworkResolver(new Psr17Factory()))
285
            ->withAddedTrustedHosts($data['hosts'] ?? [],
286
                $data['ipHeaders'] ?? [],
287
                $data['protocolHeaders'] ?? [],
288
                $data['hostHeaders'] ?? [],
289
                $data['urlHeaders'] ?? [],
290
                $data['trustedHeaders'] ?? null
291
            );
292
    }
293
}
294