diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 3f44c1c2..9fe0b675 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -241,16 +241,9 @@ - - - - - - - @@ -445,24 +438,10 @@ - - - - - - - - - - - - - - @@ -815,6 +794,7 @@ + @@ -879,6 +859,9 @@ + + + @@ -927,21 +910,6 @@ - - - - - - - - - - - - - - - diff --git a/src/SessionManager.php b/src/SessionManager.php index 9af5606b..1fbfe5c8 100644 --- a/src/SessionManager.php +++ b/src/SessionManager.php @@ -6,15 +6,20 @@ use Laminas\EventManager\Event; use Laminas\EventManager\EventManagerInterface; +use Laminas\Session\Validator\Environment; +use Laminas\Session\Validator\ValidatorInterface; use Traversable; use function array_key_exists; use function array_merge; +use function assert; use function headers_sent; use function is_array; +use function is_string; use function iterator_to_array; use function preg_match; use function register_shutdown_function; +use function serialize; use function session_destroy; use function session_id; use function session_name; @@ -24,6 +29,7 @@ use function session_status; use function session_write_close; use function setcookie; +use function unserialize; use const PHP_SESSION_ACTIVE; @@ -60,6 +66,8 @@ class SessionManager extends AbstractManager /** @var EventManagerInterface Validation chain to determine if session is valid */ protected $validatorChain; + protected array $options = []; + /** * Constructor * @@ -77,6 +85,8 @@ public function __construct( $validators = array_merge($this->defaultValidators, $validators); } + $this->options = $options; + parent::__construct($config, $storage, $saveHandler, $validators); register_shutdown_function([$this, 'writeClose']); } @@ -160,16 +170,32 @@ public function start($preserveStorage = false) */ protected function initializeValidatorChain() { - $validatorChain = $this->getValidatorChain(); - $validatorValues = $this->getStorage()->getMetadata('_VALID'); - - foreach ($this->validators as $validator) { - // Ignore validators which are already present in Storage - if (is_array($validatorValues) && array_key_exists($validator, $validatorValues)) { + /** @var array $storage */ + $storage = $this->getStorage()->getMetadata(); + + /** + * @var class-string $validatorName + */ + foreach ($this->validators as $validatorName) { + $validatorValues = $this->getStorage()->getMetadata('_VALID'); + if (is_array($validatorValues) && array_key_exists($validatorName, $validatorValues)) { continue; } - $validator = new $validator(null); + if (isset($storage['environment'])) { + assert(is_string($storage['environment'])); + /** @var Environment $initialEnvironment */ + $initialEnvironment = unserialize($storage['environment']); + } else { + $initialEnvironment = Environment::fromGlobals($_SERVER); + $this->getStorage()->setMetadata('environment', serialize($initialEnvironment)); + } + + $currentEnvironment = Environment::fromGlobals($_SERVER); + + $validatorChain = $this->getValidatorChain(); + $validator = new $validatorName($initialEnvironment, $currentEnvironment, $this->options); + $validatorChain->attach('session.validate', [$validator, 'isValid']); } } @@ -399,8 +425,7 @@ public function getValidatorChain() public function isValid() { $validator = $this->getValidatorChain(); - - $event = new Event(); + $event = new Event(); $event->setName('session.validate'); $event->setTarget($this); $event->setParams($this); diff --git a/src/Validator/Environment.php b/src/Validator/Environment.php new file mode 100644 index 00000000..a1d15552 --- /dev/null +++ b/src/Validator/Environment.php @@ -0,0 +1,38 @@ + - */ -class HttpUserAgent implements ValidatorInterface +final class HttpUserAgent implements ValidatorInterface { - /** - * Internal data - * - * @var string - */ - protected $data; - /** * Constructor * get the current user agent and store it in the session as 'valid data' - * - * @param string|null $data */ - public function __construct($data = null) - { - if ($data === null || $data === '') { - $data = $_SERVER['HTTP_USER_AGENT'] ?? null; - } - $this->data = $data; + public function __construct( + public readonly Environment $initial, + public readonly Environment $current, + array $option = [] + ) { } /** @@ -36,17 +23,7 @@ public function __construct($data = null) */ public function isValid(): bool { - $userAgent = $_SERVER['HTTP_USER_AGENT'] ?? null; - - return $userAgent === $this->getData(); - } - - /** - * Retrieve token for validating call - */ - public function getData(): mixed - { - return $this->data; + return $this->initial->userAgent === $this->current->userAgent; } /** diff --git a/src/Validator/Id.php b/src/Validator/Id.php index 92b917a9..4232cb6a 100644 --- a/src/Validator/Id.php +++ b/src/Validator/Id.php @@ -7,7 +7,6 @@ use function ini_get; use function is_numeric; use function preg_match; -use function session_id; use function trigger_error; use const E_USER_DEPRECATED; @@ -15,8 +14,6 @@ /** * session_id validator - * - * @implements ValidatorInterface */ final class Id implements ValidatorInterface { @@ -26,13 +23,11 @@ final class Id implements ValidatorInterface * Allows passing the current session_id; if none provided, uses the PHP * session_id() function to retrieve it. */ - public function __construct(protected ?string $id = null) - { - if ($id === null || $id === '') { - $id = session_id(); - } - - $this->id = $id; + public function __construct( + public readonly Environment $initial, + public readonly Environment $current, + array $options = [] + ) { } /** @@ -42,9 +37,7 @@ public function __construct(protected ?string $id = null) */ public function isValid(): bool { - $id = $this->id; - - if ($id === null) { + if ($this->current->sessionId === null) { return false; } @@ -64,15 +57,7 @@ public function isValid(): bool default => '#^[0-9a-v]*$#', }; - return (bool) preg_match($pattern, $id); - } - - /** - * Retrieve token for validating call (session_id) - */ - public function getData(): ?string - { - return $this->id; + return (bool) preg_match($pattern, $this->current->sessionId); } /** diff --git a/src/Validator/RemoteAddr.php b/src/Validator/RemoteAddr.php index 0fb35583..999acb6b 100644 --- a/src/Validator/RemoteAddr.php +++ b/src/Validator/RemoteAddr.php @@ -9,13 +9,8 @@ use function array_diff; use function array_map; use function array_pop; -use function assert; use function explode; use function in_array; -use function is_string; -use function str_replace; -use function strpos; -use function strtoupper; /** * @psalm-type OptionsArgument = array{ @@ -23,34 +18,11 @@ * trusted_proxies?: array, * proxy_header?: non-empty-string, * } - * @implements SessionValidator */ final class RemoteAddr implements SessionValidator { - /** - * Internal data. - */ - private ?string $data; - - /** - * Whether to use proxy addresses or not. - * - * As default this setting is disabled - IP address is mostly needed to increase - * security. HTTP_* are not reliable since can easily be spoofed. It can be enabled - * just for more flexibility, but if user uses proxy to connect to trusted services - * it's his/her own risk, only reliable field for IP address is $_SERVER['REMOTE_ADDR']. - */ - private bool $useProxy; - - /** - * List of trusted proxy IP addresses - */ - private array $trustedProxies; - - /** - * HTTP header to introspect for proxies - */ - private string $proxyHeader; + public ?string $initialData = null; + public ?string $currentData = null; /** * Constructor @@ -58,19 +30,18 @@ final class RemoteAddr implements SessionValidator * * @param OptionsArgument $options */ - public function __construct(?string $data = null, array $options = []) - { - $proxyHeader = $options['proxy_header'] ?? 'X_FORWARDED_FOR'; - - $this->useProxy = isset($options['use_proxy']) && $options['use_proxy']; - $this->trustedProxies = $options['trusted_proxies'] ?? []; - $this->proxyHeader = self::normalizeProxyHeader($proxyHeader); - - if ($data === null || $data === '') { - $data = $this->getIpAddress(); + public function __construct( + public readonly Environment $initial, + public readonly Environment $current, + array $options = [] + ) { + if (isset($options['use_proxy']) && $options['use_proxy'] === true) { + $this->initialData = $this->getIpAddress($this->initial, $options); + $this->currentData = $this->getIpAddress($this->current, $options); + } else { + $this->initialData = $this->initial->remoteAddr; + $this->currentData = $this->current->remoteAddr; } - - $this->data = $data; } /** @@ -79,63 +50,59 @@ public function __construct(?string $data = null, array $options = []) */ public function isValid(): bool { - return $this->getIpAddress() === $this->getData(); - } - - /** - * Checks proxy handling setting. - */ - public function getUseProxy(): bool - { - return $this->useProxy; + return $this->initialData === $this->currentData; } /** * Returns client IP address. + * + * @param OptionsArgument $options */ - private function getIpAddress(): string + public static function getIpAddress(Environment $initial, array $options = []): ?string { - $ip = $this->getIpAddressFromProxy(); + $ip = self::getIpAddressFromProxy($initial, $options); - if (false !== $ip) { + if ($ip !== false) { return $ip; } - // direct IP address - if (isset($_SERVER['REMOTE_ADDR'])) { - return $_SERVER['REMOTE_ADDR']; + if ($initial->remoteAddr !== null) { + return $initial->remoteAddr; } - return ''; + return null; } /** * Attempt to get the IP address for a proxied client * * @see http://tools.ietf.org/html/draft-ietf-appsawg-http-forwarded-10#section-5.2 + * + * @param OptionsArgument $options */ - private function getIpAddressFromProxy(): string|false + private static function getIpAddressFromProxy(Environment $initial, array $options = []): string|false { + $trustedProxies = $options['trusted_proxies'] ?? []; + if ( - ! $this->useProxy - || (isset($_SERVER['REMOTE_ADDR']) && ! in_array($_SERVER['REMOTE_ADDR'], $this->trustedProxies)) + ! (isset($options['use_proxy']) && $options['use_proxy']) + || ($initial->remoteAddr !== null && ! in_array($initial->remoteAddr, $trustedProxies)) ) { return false; } - $header = $this->proxyHeader; + $proxyHeader = $initial->forwardedFor; - if (! isset($_SERVER[$header]) || '' === $_SERVER[$header]) { + if ($proxyHeader === null || $proxyHeader === '') { return false; } // Extract IPs - assert(is_string($_SERVER[$header])); - $ips = explode(',', $_SERVER[$header]); + $ips = explode(',', $proxyHeader); // trim, so we can compare against trusted proxies properly $ips = array_map('trim', $ips); // remove trusted proxy IPs - $ips = array_diff($ips, $this->trustedProxies); + $ips = array_diff($ips, $trustedProxies); // Any left? if (empty($ips)) { return false; @@ -149,30 +116,6 @@ private function getIpAddressFromProxy(): string|false return array_pop($ips); } - /** - * Normalize a header string - * - * Normalizes a header string to a format that is compatible with - * $_SERVER - */ - protected static function normalizeProxyHeader(string $header): string - { - $header = strtoupper($header); - $header = str_replace('-', '_', $header); - if (0 !== strpos($header, 'HTTP_')) { - $header = 'HTTP_' . $header; - } - return $header; - } - - /** - * Retrieve token for validating call - */ - public function getData(): ?string - { - return $this->data; - } - /** * Return validator name */ diff --git a/src/Validator/ValidatorInterface.php b/src/Validator/ValidatorInterface.php index 3c5863d9..cf08e264 100644 --- a/src/Validator/ValidatorInterface.php +++ b/src/Validator/ValidatorInterface.php @@ -7,10 +7,13 @@ /** * Session validator interface * - * @template T + * @psalm-import-type OptionsArgument from RemoteAddr */ interface ValidatorInterface { + /** @param OptionsArgument $options */ + public function __construct(Environment $initial, Environment $current, array $options = []); + /** * This method will be called at the beginning of * every session to determine if the current environment matches @@ -18,13 +21,6 @@ interface ValidatorInterface */ public function isValid(): bool; - /** - * Get data from validator to be used for validation comparisons - * - * @return T - */ - public function getData(): mixed; - /** * Get validator name for use with storing validators between requests */ diff --git a/src/ValidatorChain.php b/src/ValidatorChain.php index 8c00e3c9..03a41d69 100644 --- a/src/ValidatorChain.php +++ b/src/ValidatorChain.php @@ -17,12 +17,6 @@ class ValidatorChain extends EventManager public function __construct(protected StorageInterface $storage) { parent::__construct(); - $validators = $storage->getMetadata('_VALID'); - if ($validators) { - foreach ($validators as $validator => $data) { - $this->attachValidator('session.validate', [new $validator($data), 'isValid'], 1); - } - } } /** @@ -69,11 +63,9 @@ private function attachValidator($event, $callback, $priority) array_unshift($callback, $test); } if ($context instanceof ValidatorInterface) { - $data = $context->getData(); $name = $context->getName(); - $this->getStorage()->setMetadata('_VALID', [$name => $data]); + $this->getStorage()->setMetadata('_VALID', [$name]); } - return parent::attach($event, $callback, $priority); } } diff --git a/test/Service/SessionManagerFactoryTest.php b/test/Service/SessionManagerFactoryTest.php index a8df7447..affc5bd1 100644 --- a/test/Service/SessionManagerFactoryTest.php +++ b/test/Service/SessionManagerFactoryTest.php @@ -184,9 +184,10 @@ public function testFactoryDoesNotAttachValidatorTwoTimes(): void $storage->setMetadata( '_VALID', [ - Validator\RemoteAddr::class => '1.2.3.4', + Validator\RemoteAddr::class, ] ); + $this->services->setService(StorageInterface::class, $storage); $this->services->setService( 'config', @@ -212,7 +213,6 @@ public function testFactoryDoesNotAttachValidatorTwoTimes(): void $chain = $manager->getValidatorChain(); self::assertInstanceOf(EventManager::class, $chain); - $listeners = iterator_to_array($this->getListenersForEvent('session.validate', $chain)); self::assertCount(2, $listeners); diff --git a/test/SessionManagerTest.php b/test/SessionManagerTest.php index d788cbd6..aad3385f 100644 --- a/test/SessionManagerTest.php +++ b/test/SessionManagerTest.php @@ -14,15 +14,18 @@ use Laminas\Session\Storage\ArrayStorage; use Laminas\Session\Storage\SessionArrayStorage; use Laminas\Session\Storage\SessionStorage; +use Laminas\Session\Validator\Environment; use Laminas\Session\Validator\Id; use Laminas\Session\Validator\RemoteAddr; use LaminasTest\Session\TestAsset\Php81CompatibleStorageInterface; +use LaminasTest\Session\TestAsset\TestFailingValidator; use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\Attributes\RunInSeparateProcess; use PHPUnit\Framework\TestCase; use Traversable; use function array_merge; +use function assert; use function extension_loaded; use function headers_sent; use function ini_get; @@ -38,6 +41,7 @@ use function set_error_handler; use function stristr; use function uniqid; +use function unserialize; use function var_export; use function xdebug_get_headers; @@ -712,17 +716,23 @@ public function testStartingSessionThatFailsAValidatorShouldRaiseException(): vo { $this->manager = new SessionManager(); $chain = $this->manager->getValidatorChain(); - $chain->attach('session.validate', [new TestAsset\TestFailingValidator(), 'isValid']); + $chain->attach('session.validate', [ + new TestFailingValidator( + Environment::fromGlobals($_SERVER), + Environment::fromGlobals($_SERVER) + ), + 'isValid', + ]); $this->expectException(RuntimeException::class); $this->expectExceptionMessage('failed'); $this->manager->start(); } #[RunInSeparateProcess] + #[IgnoreDeprecations] public function testResumeSessionThatFailsAValidatorShouldRaiseException(): void { - $this->manager = new SessionManager(); - $this->manager->setSaveHandler(new TestAsset\TestSaveHandlerWithValidator()); + $this->manager = new SessionManager(validators: [TestFailingValidator::class]); $this->expectException(RuntimeException::class); $this->expectExceptionMessage('failed'); $this->manager->start(); @@ -774,16 +784,25 @@ public function testProducedSessionManagerWillNotReplaceSessionSuperGlobalValues public function testValidatorChainSessionMetadataIsPreserved(): void { $this->manager = new SessionManager(); - $this->manager->getValidatorChain() - ->attach('session.validate', [new RemoteAddr(), 'isValid']); - self::assertFalse($this->manager->sessionExists()); - $this->manager->start(); + $environment = unserialize((string) $this->manager->getStorage()->getMetadata('environment')); + assert($environment instanceof Environment); + $this->manager->getValidatorChain() + ->attach('session.validate', [ + new RemoteAddr( + $environment, + Environment::fromGlobals($_SERVER) + ), + 'isValid', + ]); self::assertIsArray($_SESSION['__Laminas']['_VALID']); - self::assertArrayHasKey(RemoteAddr::class, $_SESSION['__Laminas']['_VALID']); - self::assertEquals('', $_SESSION['__Laminas']['_VALID'][RemoteAddr::class]); + self::assertIsString($_SESSION['__Laminas']['_VALID'][0]); + self::assertEquals( + Environment::fromGlobals($_SERVER), + unserialize((string) $_SESSION['__Laminas']['environment']) + ); } #[RunInSeparateProcess] @@ -791,7 +810,13 @@ public function testRemoteAddressValidationWillFailOnInvalidAddress(): void { $this->manager = new SessionManager(); $this->manager->getValidatorChain() - ->attach('session.validate', [new RemoteAddr('123.123.123.123'), 'isValid']); + ->attach('session.validate', [ + new RemoteAddr( + Environment::fromGlobals($_SERVER), + new Environment(remoteAddr: '123.123.123.123') + ), + 'isValid', + ]); $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Session validation failed'); @@ -806,7 +831,7 @@ public function testRemoteAddressValidationWillSucceedWithValidPreSetData(): voi $_SESSION = [ '__Laminas' => [ '_VALID' => [ - RemoteAddr::class => '', + RemoteAddr::class, ], ], ]; @@ -816,30 +841,19 @@ public function testRemoteAddressValidationWillSucceedWithValidPreSetData(): voi self::assertTrue($this->manager->isValid()); } - #[RunInSeparateProcess] - public function testRemoteAddressValidationWillFailWithInvalidPreSetData(): void - { - $this->manager = new SessionManager(); - $_SESSION = [ - '__Laminas' => [ - '_VALID' => [ - RemoteAddr::class => '123.123.123.123', - ], - ], - ]; - - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Session validation failed'); - $this->manager->start(); - } - #[RunInSeparateProcess] #[IgnoreDeprecations] public function testIdValidationWillFailOnInvalidData(): void { $this->manager = new SessionManager(); $this->manager->getValidatorChain() - ->attach('session.validate', [new Id('invalid-value'), 'isValid']); + ->attach('session.validate', [ + new Id( + Environment::fromGlobals($_SERVER), + new Environment(sessionId: 'invalid_value') + ), + 'isValid', + ]); $this->expectException(RuntimeException::class); $this->expectExceptionMessage('Session validation failed'); diff --git a/test/TestAsset/TestFailingValidator.php b/test/TestAsset/TestFailingValidator.php index 60122ecd..9a81ec6b 100644 --- a/test/TestAsset/TestFailingValidator.php +++ b/test/TestAsset/TestFailingValidator.php @@ -4,18 +4,16 @@ namespace LaminasTest\Session\TestAsset; +use Laminas\Session\Validator\Environment; use Laminas\Session\Validator\ValidatorInterface; -/** - * @implements ValidatorInterface - */ final class TestFailingValidator implements ValidatorInterface { - public function getData(): mixed + public function __construct(Environment $initial, Environment $current, array $options = []) { - return false; } + public ?Environment $current = null; public function getName(): string { return self::class; @@ -23,6 +21,6 @@ public function getName(): string public function isValid(): bool { - return $this->getData(); + return false; } } diff --git a/test/TestAsset/TestSaveHandlerWithValidator.php b/test/TestAsset/TestSaveHandlerWithValidator.php deleted file mode 100644 index 880b33d3..00000000 --- a/test/TestAsset/TestSaveHandlerWithValidator.php +++ /dev/null @@ -1,63 +0,0 @@ -getData()); self::assertTrue($validator->isValid()); } @@ -23,16 +25,14 @@ public function testIsValidWhenNoUserAgentIsSet(): void { // technically not needed in CLI unset($_SERVER['HTTP_USER_AGENT']); + $validator = new HttpUserAgent(new Environment(userAgent: null), Environment::fromGlobals($_SERVER)); - $validator = new HttpUserAgent(); - - self::assertNull($validator->getData()); self::assertTrue($validator->isValid()); } public function testGetNameReturnsClassName(): void { - $validator = new HttpUserAgent(); + $validator = new HttpUserAgent(new Environment(userAgent: null), Environment::fromGlobals($_SERVER)); self::assertSame(HttpUserAgent::class, $validator->getName()); } diff --git a/test/Validator/IdTest.php b/test/Validator/IdTest.php index 9cf85721..8e300958 100644 --- a/test/Validator/IdTest.php +++ b/test/Validator/IdTest.php @@ -5,6 +5,7 @@ namespace LaminasTest\Session\Validator; +use Laminas\Session\Validator\Environment; use Laminas\Session\Validator\Id; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\IgnoreDeprecations; @@ -39,17 +40,10 @@ public function testIsValidPhp71(int $bitsPerCharacter, string $id, bool $isVali { ini_set('session.sid_bits_per_character', $bitsPerCharacter); - $validator = new Id($id); + $validator = new Id(Environment::fromGlobals($_SERVER), new Environment(sessionId: $id)); self::assertSame($isValid, $validator->isValid()); } - public function testConstructorSetId(): void - { - $id = new Id('1234'); - - self::assertSame('1234', $id->getData()); - } - /** * @runInSeparateProcess */ @@ -57,15 +51,14 @@ public function testInitializedWithSessionIdWhenIdIsNotPassed(): void { session_start(); $sessionId = session_id(); + $id = new Id(Environment::fromGlobals($_SERVER), Environment::fromGlobals($_SERVER)); - $id = new Id(); - - self::assertSame($sessionId, $id->getData()); + self::assertSame($sessionId, $id->initial->sessionId); } public function testValidatorName(): void { - $id = new Id(); + $id = new Id(Environment::fromGlobals($_SERVER), Environment::fromGlobals($_SERVER)); self::assertSame(Id::class, $id->getName()); } diff --git a/test/Validator/RemoteAddrTest.php b/test/Validator/RemoteAddrTest.php index c2ef3388..40afc590 100644 --- a/test/Validator/RemoteAddrTest.php +++ b/test/Validator/RemoteAddrTest.php @@ -4,6 +4,7 @@ namespace LaminasTest\Session\Validator; +use Laminas\Session\Validator\Environment; use Laminas\Session\Validator\RemoteAddr; use PHPUnit\Framework\TestCase; @@ -12,14 +13,7 @@ */ class RemoteAddrTest extends TestCase { - protected array $backup; - - protected RemoteAddr $defaultRemoteAddr; - - protected function setUp(): void - { - $this->defaultRemoteAddr = new RemoteAddr(); - } + protected array $backup = []; protected function backup(): void { @@ -36,23 +30,13 @@ protected function restore(): void $_SERVER = $this->backup; } - public function testGetData(): void - { - $validator = new RemoteAddr('0.1.2.3'); - self::assertEquals('0.1.2.3', $validator->getData()); - } - - public function testDefaultUseProxy(): void - { - self::assertFalse($this->defaultRemoteAddr->getUseProxy()); - } - public function testRemoteAddrWithoutProxy(): void { $this->backup(); $_SERVER['REMOTE_ADDR'] = '0.1.2.3'; - $validator = new RemoteAddr(); - self::assertEquals('0.1.2.3', $validator->getData()); + $validator = + new RemoteAddr(Environment::fromGlobals($_SERVER), Environment::fromGlobals($_SERVER)); + self::assertEquals('0.1.2.3', $validator->current->remoteAddr); $this->restore(); } @@ -60,8 +44,8 @@ public function testIsValid(): void { $this->backup(); $_SERVER['REMOTE_ADDR'] = '0.1.2.3'; - $validator = new RemoteAddr(); - $_SERVER['REMOTE_ADDR'] = '1.1.2.3'; + $validator = + new RemoteAddr(new Environment(remoteAddr: '0.1.1.3'), Environment::fromGlobals($_SERVER)); self::assertFalse($validator->isValid()); $this->restore(); } @@ -71,8 +55,11 @@ public function testIgnoreProxyByDefault(): void $this->backup(); $_SERVER['REMOTE_ADDR'] = '0.1.2.3'; $_SERVER['HTTP_CLIENT_IP'] = '1.1.2.3'; - $validator = new RemoteAddr(); - self::assertEquals('0.1.2.3', $validator->getData()); + $validator = new RemoteAddr( + Environment::fromGlobals($_SERVER), + Environment::fromGlobals($_SERVER) + ); + self::assertEquals('0.1.2.3', $validator->current->remoteAddr); $this->restore(); } @@ -85,8 +72,13 @@ public function testHttpXForwardedFor(): void 'use_proxy' => true, 'trusted_proxies' => ['0.1.2.3'], ]; - $validator = new RemoteAddr(null, $options); - self::assertEquals('1.1.2.3', $validator->getData()); + + $validator = new RemoteAddr( + Environment::fromGlobals($_SERVER), + Environment::fromGlobals($_SERVER), + $options + ); + self::assertEquals('1.1.2.3', $validator->currentData); $this->restore(); } @@ -102,8 +94,12 @@ public function testHttpClientIp(): void 'trusted_proxies' => ['0.1.2.3'], ]; - $validator = new RemoteAddr(null, $options); - self::assertEquals('2.1.2.3', $validator->getData()); + $validator = new RemoteAddr( + Environment::fromGlobals($_SERVER), + Environment::fromGlobals($_SERVER), + $options + ); + self::assertEquals('2.1.2.3', $validator->currentData); $this->restore(); } @@ -118,8 +114,13 @@ public function testUsesRightMostAddressWhenMultipleHttpXForwardedForAddressesPr 'trusted_proxies' => ['0.1.2.3'], ]; - $validator = new RemoteAddr(null, $options); - self::assertEquals('1.1.2.3', $validator->getData()); + $validator = new RemoteAddr( + Environment::fromGlobals($_SERVER), + Environment::fromGlobals($_SERVER), + $options + ); + + self::assertEquals('1.1.2.3', $validator->currentData); $this->restore(); } @@ -134,8 +135,12 @@ public function testShouldNotUseClientIpHeaderToTestProxyCapabilitiesByDefault() 'trusted_proxies' => ['0.1.2.3'], ]; - $validator = new RemoteAddr(null, $options); - self::assertEquals('0.1.2.3', $validator->getData()); + $validator = new RemoteAddr( + Environment::fromGlobals($_SERVER), + Environment::fromGlobals($_SERVER), + $options + ); + self::assertEquals('0.1.2.3', $validator->current->remoteAddr); $this->restore(); } @@ -150,8 +155,12 @@ public function testWillOmitTrustedProxyIpsFromXForwardedForMatching(): void 'trusted_proxies' => ['1.1.2.3'], ]; - $validator = new RemoteAddr(null, $options); - self::assertEquals('2.1.2.3', $validator->getData()); + $validator = new RemoteAddr( + Environment::fromGlobals($_SERVER), + Environment::fromGlobals($_SERVER), + $options + ); + self::assertEquals('2.1.2.3', $validator->currentData); $this->restore(); } @@ -167,14 +176,22 @@ public function testCanSpecifyWhichHeaderToUseStatically(): void 'proxy_header' => 'Client-Ip', ]; - $validator = new RemoteAddr(null, $options); - self::assertEquals('0.1.2.3', $validator->getData()); + $validator = new RemoteAddr( + Environment::fromGlobals($_SERVER), + Environment::fromGlobals($_SERVER), + $options + ); + self::assertEquals('0.1.2.3', $validator->current->remoteAddr); $this->restore(); } public function testGetName(): void { - self::assertEquals(RemoteAddr::class, $this->defaultRemoteAddr->getName()); + $validator = new RemoteAddr( + Environment::fromGlobals($_SERVER), + Environment::fromGlobals($_SERVER) + ); + self::assertEquals(RemoteAddr::class, $validator->getName()); } public function testUnknownServerHeader(): void @@ -186,8 +203,12 @@ public function testUnknownServerHeader(): void 'proxy_header' => 'Unknown-Header', ]; - $validator = new RemoteAddr(null, $options); - self::assertEmpty($validator->getData()); + $validator = new RemoteAddr( + Environment::fromGlobals($_SERVER), + Environment::fromGlobals($_SERVER), + $options + ); + self::assertEmpty($validator->current->remoteAddr); $this->restore(); } } diff --git a/test/Validator/StaticValidatorStub.php b/test/Validator/StaticValidatorStub.php deleted file mode 100644 index 37209c58..00000000 --- a/test/Validator/StaticValidatorStub.php +++ /dev/null @@ -1,31 +0,0 @@ - - */ -class StaticValidatorStub implements ValidatorInterface -{ - public static int $isValidCallCount = 0; - - public function isValid(): bool - { - self::$isValidCallCount++; - return $this->getData(); - } - - public function getData(): mixed - { - return false; - } - - public function getName(): string - { - return self::class; - } -} diff --git a/test/Validator/ValidatorChainTest.php b/test/Validator/ValidatorChainTest.php index e74efbf1..de530588 100644 --- a/test/Validator/ValidatorChainTest.php +++ b/test/Validator/ValidatorChainTest.php @@ -5,6 +5,7 @@ namespace LaminasTest\Session\Validator; use Laminas\Session\Storage\ArrayStorage; +use Laminas\Session\Validator\Environment; use Laminas\Session\ValidatorChain; use LaminasTest\Session\TestAsset\TestFailingValidator; use PHPUnit\Framework\TestCase; @@ -26,25 +27,11 @@ public function testGetStorage(): void public function testAttachValidator(): void { - $validator = new TestFailingValidator(); + $validator = new TestFailingValidator(Environment::fromGlobals($_SERVER), Environment::fromGlobals($_SERVER)); $this->validatorChain->attach('test', [$validator, 'isValid']); $validatorMetadata = $this->validatorChain->getStorage()->getMetadata('_VALID'); self::assertIsArray($validatorMetadata); - self::assertArrayHasKey($validator->getName(), $validatorMetadata); - self::assertSame($validatorMetadata[$validator->getName()], $validator->getData()); - } - - public function testExistingValidatorsAreAttached(): void - { - $validator = new StaticValidatorStub(); - $storage = new ArrayStorage(); - $storage->setMetadata('_VALID', [$validator::class => $validator->getData()]); - - $this->validatorChain = new ValidatorChain($storage); - - $this->validatorChain->trigger('session.validate'); - self::assertSame(1, $validator::$isValidCallCount); } }