Completed
Pull Request — master (#157)
by
unknown
02:01
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
        ];
106
    }
107
108
    /**
109
     * @dataProvider trustedDataProvider
110
     */
111
    public function testTrusted(
112
        array $headers,
113
        array $serverParams,
114
        array $trustedHosts,
115
        string $expectedClientIp,
116
        ?string $expectedHttpHost = null,
117
        string $expectedHttpScheme = 'http',
118
        string $expectedPath = '/',
119
        string $expectedQuery = ''
120
    ): void {
121
        $request = $this->newRequestWithSchemaAndHeaders('http', $headers, $serverParams);
122
        $requestHandler = new MockRequestHandler();
123
124
        $middleware = new TrustedHostsNetworkResolver(new Psr17Factory());
125
        foreach ($trustedHosts as $data) {
126
            $middleware = $middleware->withAddedTrustedHosts(
127
                $data['hosts'],
128
                $data['ipHeaders'] ?? [],
129
                $data['protocolHeaders'] ?? [],
130
                $data['hostHeaders'] ?? [],
131
                $data['urlHeaders'] ?? [],
132
                $data['trustedHeaders'] ?? null);
133
        }
134
        $response = $middleware->process($request, $requestHandler);
135
        $this->assertSame(200, $response->getStatusCode());
136
        $this->assertSame($expectedClientIp, $requestHandler->processedRequest->getAttribute('requestClientIp'));
137
        if ($expectedHttpHost !== null) {
138
            $this->assertSame($expectedHttpHost, $requestHandler->processedRequest->getUri()->getHost());
139
        }
140
        $this->assertSame($expectedHttpScheme, $requestHandler->processedRequest->getUri()->getScheme());
141
        $this->assertSame($expectedPath, $requestHandler->processedRequest->getUri()->getPath());
142
        $this->assertSame($expectedQuery, $requestHandler->processedRequest->getUri()->getQuery());
143
    }
144
145
    public function notTrustedDataProvider(): array
146
    {
147
        return [
148
            'none' => [
149
                [],
150
                ['REMOTE_ADDR' => '127.0.0.1'],
151
                [],
152
            ],
153
            'x-forwarded-for' => [
154
                ['x-forwarded-for' => ['9.9.9.9', '5.5.5.5', '2.2.2.2']],
155
                ['REMOTE_ADDR' => '127.0.0.1'],
156
                [['hosts' => ['8.8.8.8'], 'ipHeaders' => ['x-forwarded-for']]],
157
            ],
158
            'rfc7239' => [
159
                ['x-forwarded-for' => ['for=9.9.9.9', 'for=5.5.5.5', 'for=2.2.2.2']],
160
                ['REMOTE_ADDR' => '127.0.0.1'],
161
                [['hosts' => ['8.8.8.8'], 'ipHeaders' => ['x-forwarded-for']]],
162
            ],
163
        ];
164
    }
165
166
    /**
167
     * @dataProvider notTrustedDataProvider
168
     */
169
    public function testNotTrusted(array $headers, array $serverParams, array $trustedHosts): void
170
    {
171
        $request = $this->newRequestWithSchemaAndHeaders('http', $headers, $serverParams);
172
        $requestHandler = new MockRequestHandler();
173
174
        $middleware = new TrustedHostsNetworkResolver(new Psr17Factory());
175
        foreach ($trustedHosts as $data) {
176
            $middleware = $middleware->withAddedTrustedHosts(
177
                $data['hosts'],
178
                $data['ipHeaders'] ?? [],
179
                $data['protocolHeaders'] ?? [],
180
                [],
181
                [],
182
                $data['trustedHeaders'] ?? []);
183
        }
184
        $response = $middleware->process($request, $requestHandler);
185
        $this->assertSame(412, $response->getStatusCode());
186
    }
187
188
    public function testNotTrustedMiddleware(): void
189
    {
190
        $request = $this->newRequestWithSchemaAndHeaders('http', [], [
191
            'REMOTE_ADDR' => '127.0.0.1',
192
        ]);
193
        $requestHandler = new MockRequestHandler();
194
195
        $middleware = new TrustedHostsNetworkResolver(new Psr17Factory());
196
        $content = 'Another branch.';
197
        $middleware = $middleware->withNotTrustedBranch(new class($content) implements MiddlewareInterface
198
        {
199
            private $content;
200
201
            public function __construct(string $content)
202
            {
203
                $this->content = $content;
204
            }
205
206
            public function process(
207
                ServerRequestInterface $request,
208
                RequestHandlerInterface $handler
209
            ): ResponseInterface {
210
                $response = (new Psr17Factory())->createResponse(403);
211
                $response->getBody()->write($this->content);
212
                return $response;
213
            }
214
        });
215
        $response = $middleware->process($request, $requestHandler);
216
        $this->assertInstanceOf(ResponseInterface::class, $response);
217
        $this->assertSame(403, $response->getStatusCode());
218
        $body = $response->getBody();
219
        $body->rewind();
220
        $this->assertSame($content, $body->getContents());
221
    }
222
223
    public function addedTrustedHostsInvalidParameterDataProvider(): array
224
    {
225
        return [
226
            'hostsEmpty' => ['hosts' => []],
227
            'hostsEmptyString' => ['hosts' => ['']],
228
            'hostsNumeric' => ['hosts' => [888]],
229
            'hostsSpaces' => ['hosts' => ['    ']],
230
            'hostsNotDomain' => ['host' => ['-apple']],
231
            'urlHeadersEmpty' => ['urlHeaders' => ['']],
232
            'urlHeadersNumeric' => ['urlHeaders' => [888]],
233
            'urlHeadersSpaces' => ['urlHeaders' => ['   ']],
234
            'trustedHeadersEmpty' => ['trustedHeaders' => ['']],
235
            'trustedHeadersNumeric' => ['trustedHeaders' => [888]],
236
            'trustedHeadersSpaces' => ['trustedHeaders' => ['   ']],
237
            'protocolHeadersNumeric' => ['protocolHeaders' => ['http' => 888]],
238
            'ipHeadersEmptyString' => ['ipHeaders' => [' ']],
239
            'ipHeadersNumeric' => ['ipHeaders' => [888]],
240
            'ipHeadersInvalidType' => ['ipHeaders' => [['---', 'aaa']]],
241
            'ipHeadersInvalidTypeValue' => [
242
                'ipHeaders' => [
243
                    [
244
                        TrustedHostsNetworkResolver::IP_HEADER_TYPE_RFC7239,
245
                        888
246
                    ]
247
                ]
248
            ],
249
        ];
250
    }
251
252
    /**
253
     * @dataProvider addedTrustedHostsInvalidParameterDataProvider
254
     */
255
    public function testAddedTrustedHostsInvalidParameter(array $data): void
256
    {
257
        $this->expectException(\InvalidArgumentException::class);
258
        (new TrustedHostsNetworkResolver(new Psr17Factory()))
259
            ->withAddedTrustedHosts($data['hosts'] ?? [],
260
                $data['ipHeaders'] ?? [],
261
                $data['protocolHeaders'] ?? [],
262
                $data['hostHeaders'] ?? [],
263
                $data['urlHeaders'] ?? [],
264
                $data['trustedHeaders'] ?? null
265
            );
266
    }
267
}
268