Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## 1.0.2 under development

- no changes in this release.
- Enh #42: Replace `yiisoft/validator` dependency to `yiisoft/network-utilities` (@vjik)

## 1.0.1 June 02, 2024

Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"psr/http-server-handler": "^1.0",
"psr/http-server-middleware": "^1.0",
"yiisoft/http": "^1.2",
"yiisoft/validator": "^1.0"
"yiisoft/network-utilities": "^1.1"
},
"require-dev": {
"httpsoft/http-message": "^1.0",
Expand Down
37 changes: 37 additions & 0 deletions src/IpValidator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

declare(strict_types=1);

namespace Yiisoft\ProxyMiddleware;

use Yiisoft\NetworkUtilities\IpHelper;
use Yiisoft\NetworkUtilities\IpRanges;

/**
* @internal
*/
final class IpValidator
{
public static function isIp(string $value): bool
{
return self::isIpV4($value) || self::isIpV6($value);
}

public static function isIpV6(string $value): bool
{
return preg_match(IpHelper::IPV6_REGEXP, $value) === 1;
}

/**
* @param string[] $ranges
*/
public static function inRanges(string $value, array $ranges): bool
{
return (new IpRanges($ranges))->isAllowed($value);
}

private static function isIpV4(string $value): bool
{
return preg_match(IpHelper::IPV4_REGEXP, $value) === 1;
}
}
40 changes: 8 additions & 32 deletions src/TrustedHostsNetworkResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,9 @@
use Psr\Http\Server\RequestHandlerInterface;
use RuntimeException;
use Yiisoft\Http\HeaderValueHelper;
use Yiisoft\NetworkUtilities\IpRanges;
use Yiisoft\ProxyMiddleware\Exception\InvalidConnectionChainItemException;
use Yiisoft\ProxyMiddleware\Exception\RfcProxyParseException;
use Yiisoft\Validator\Rule\Ip;
use Yiisoft\Validator\ValidatorInterface;

use function count;
use function in_array;
Expand Down Expand Up @@ -130,7 +129,6 @@ class TrustedHostsNetworkResolver implements MiddlewareInterface

/**
* @psalm-var list<non-empty-string>
* @psalm-suppress PropertyNotSetInConstructor
*/
private array $trustedIps = [];
/**
Expand All @@ -146,7 +144,7 @@ class TrustedHostsNetworkResolver implements MiddlewareInterface
*/
private ?string $connectionChainItemsAttribute = null;

public function __construct(private ValidatorInterface $validator)
public function __construct()
{
}

Expand All @@ -165,7 +163,7 @@ public function withTrustedIps(array $trustedIps): self
foreach ($trustedIps as $ip) {
$this->assertIsNonEmptyString($ip, 'Trusted IP');

if (!$this->isIp($ip)) {
if (!IpValidator::isIp($ip)) {
throw new InvalidArgumentException("\"$ip\" is not a valid IP.");
}

Expand Down Expand Up @@ -672,7 +670,7 @@ private function parseProxiesFromRfcHeader(array $proxyItems): array
if (isset($matches['ipv6']) && !empty($matches['ipv6'])) {
$ip = $matches['ipv6'];

if (!$this->isIpv6($ip)) {
if (!IpValidator::isIpV6($ip)) {
$message = "Enclosing in square brackets assumes presence of valid IPv6, \"$ip\" given.";

throw new RfcProxyParseException($message);
Expand Down Expand Up @@ -722,7 +720,7 @@ private function getConnectionChainItem(
bool $validateIp = true,
bool $validateProtocol = true,
): array {
if ($ip !== null && $validateIp && !$this->isIp($ip)) {
if ($ip !== null && $validateIp && !IpValidator::isIp($ip)) {
throw new InvalidConnectionChainItemException("\"$ip\" is not a valid IP.");
}

Expand Down Expand Up @@ -847,47 +845,25 @@ private function assertReverseObfuscatedIpData(?array $ipData): void
}
}

if (!$this->isIp($ipData[0])) {
if (!IpValidator::isIp($ipData[0])) {
throw new RuntimeException('IP returned from reverse-obfuscated IP data is not valid.');
}
}

/**
* @psalm-assert non-empty-string $value
*/
private function isIp(string $value): bool
{
return $this
->validator
->validate($value, [new Ip()])
->isValid();
}

/**
* @psalm-assert non-empty-string $value
*/
private function isIpv6(string $value): bool
{
return $this
->validator
->validate($value, [new Ip(allowIpv4: false)])
->isValid();
}

/**
* @psalm-param non-empty-string $value
*/
private function isPrivateIp(string $value): bool
{
return (new Ip(ranges: ['private']))->isAllowed($value);
return IpValidator::inRanges($value, [IpRanges::PRIVATE]);
}

/**
* @psalm-param non-empty-string $value
*/
private function isTrustedIp(string $value): bool
{
return !empty($this->trustedIps) && (new Ip(ranges: $this->trustedIps))->isAllowed($value);
return !empty($this->trustedIps) && IpValidator::inRanges($value, $this->trustedIps);
}

/**
Expand Down
3 changes: 1 addition & 2 deletions tests/TrustedHostsNetworkResolver/ProcessTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
use Yiisoft\Http\Status;
use Yiisoft\ProxyMiddleware\Tests\Support\MockRequestHandler;
use Yiisoft\ProxyMiddleware\TrustedHostsNetworkResolver;
use Yiisoft\Validator\Validator;

final class ProcessTest extends TestCase
{
Expand Down Expand Up @@ -1326,7 +1325,7 @@ public function dataProcess(): iterable
],
],
yield 'RFC header, IP related data, hidden IP, obfuscated, reverse-obfuscating' => [
(new class (new Validator()) extends TrustedHostsNetworkResolver {
(new class () extends TrustedHostsNetworkResolver {
protected function reverseObfuscateIpIdentifier(
string $ipIdentifier,
array $validatedConnectionChainItems,
Expand Down
15 changes: 7 additions & 8 deletions tests/TrustedHostsNetworkResolver/RuntimeExceptionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
use Yiisoft\ProxyMiddleware\Exception\RfcProxyParseException;
use Yiisoft\ProxyMiddleware\Tests\Support\MockRequestHandler;
use Yiisoft\ProxyMiddleware\TrustedHostsNetworkResolver;
use Yiisoft\Validator\Validator;

final class RuntimeExceptionTest extends TestCase
{
Expand Down Expand Up @@ -509,7 +508,7 @@ public function dataReverseObfuscateIpIdentifierException(): iterable
{
return [
yield 'empty array' => [
(new class (new Validator()) extends TrustedHostsNetworkResolver {
(new class () extends TrustedHostsNetworkResolver {
protected function reverseObfuscateIpIdentifier(
string $ipIdentifier,
array $validatedConnectionChainItems,
Expand All @@ -533,7 +532,7 @@ protected function reverseObfuscateIpIdentifier(
'Reverse-obfuscated IP data can\'t be empty.',
],
yield 'wrong items count' => [
(new class (new Validator()) extends TrustedHostsNetworkResolver {
(new class () extends TrustedHostsNetworkResolver {
protected function reverseObfuscateIpIdentifier(
string $ipIdentifier,
array $validatedConnectionChainItems,
Expand All @@ -557,7 +556,7 @@ protected function reverseObfuscateIpIdentifier(
'Invalid array keys for reverse-obfuscated IP data. The allowed and required keys are: "0", "1".',
],
yield 'IP: not a string' => [
(new class (new Validator()) extends TrustedHostsNetworkResolver {
(new class () extends TrustedHostsNetworkResolver {
protected function reverseObfuscateIpIdentifier(
string $ipIdentifier,
array $validatedConnectionChainItems,
Expand All @@ -581,7 +580,7 @@ protected function reverseObfuscateIpIdentifier(
'IP returned from reverse-obfuscated IP data must be non-empty string.',
],
yield 'IP: empty string' => [
(new class (new Validator()) extends TrustedHostsNetworkResolver {
(new class () extends TrustedHostsNetworkResolver {
protected function reverseObfuscateIpIdentifier(
string $ipIdentifier,
array $validatedConnectionChainItems,
Expand All @@ -605,7 +604,7 @@ protected function reverseObfuscateIpIdentifier(
'IP returned from reverse-obfuscated IP data must be non-empty string.',
],
yield 'IP: invalid' => [
(new class (new Validator()) extends TrustedHostsNetworkResolver {
(new class () extends TrustedHostsNetworkResolver {
protected function reverseObfuscateIpIdentifier(
string $ipIdentifier,
array $validatedConnectionChainItems,
Expand All @@ -629,7 +628,7 @@ protected function reverseObfuscateIpIdentifier(
'IP returned from reverse-obfuscated IP data is not valid.',
],
yield 'port: empty string' => [
(new class (new Validator()) extends TrustedHostsNetworkResolver {
(new class () extends TrustedHostsNetworkResolver {
protected function reverseObfuscateIpIdentifier(
string $ipIdentifier,
array $validatedConnectionChainItems,
Expand All @@ -653,7 +652,7 @@ protected function reverseObfuscateIpIdentifier(
'Port returned from reverse-obfuscated IP data must be non-empty string.',
],
yield 'IP: valid port instead of IP, port: invalid' => [
(new class (new Validator()) extends TrustedHostsNetworkResolver {
(new class () extends TrustedHostsNetworkResolver {
protected function reverseObfuscateIpIdentifier(
string $ipIdentifier,
array $validatedConnectionChainItems,
Expand Down
5 changes: 1 addition & 4 deletions tests/TrustedHostsNetworkResolver/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,12 @@
use PHPUnit\Framework\TestCase as PHPUnitTestCase;
use Psr\Http\Message\ServerRequestInterface;
use Yiisoft\ProxyMiddleware\TrustedHostsNetworkResolver;
use Yiisoft\Validator\Validator;

class TestCase extends PHPUnitTestCase
{
protected function createMiddleware(): TrustedHostsNetworkResolver
{
$validator = new Validator();

return new TrustedHostsNetworkResolver($validator);
return new TrustedHostsNetworkResolver();
}

protected function createRequest(
Expand Down