. */ namespace Ampache\Module\Util; use Ampache\Module\System\LegacyLogger; use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; #[RunTestsInSeparateProcesses] class RequestParserTest extends TestCase { private LoggerInterface $logger; private RequestParser $subject; /** * A token that survives its own use can be replayed, which is the whole point of having one. */ public function testAFormTokenWorksExactlyOnce(): void { $formName = 'some-form'; $sid = 'some-sid'; $_POST['form_validation'] = $sid; $_SESSION['forms'][$sid] = ['name' => $formName, 'expire' => time() + 3600]; self::assertTrue($this->subject->verifyForm($formName)); self::assertFalse($this->subject->verifyForm($formName), 'the second submit must be refused'); self::assertArrayNotHasKey($sid, $_SESSION['forms']); } /** * A third-party authorization brings the visitor back with a GET, so the token has to be readable * there -- and consumed just the same. */ public function testATokenCarriedInTheQueryStringIsAcceptedAndConsumed(): void { $formName = 'grant'; $sid = 'some-sid'; $_GET['form_validation'] = $sid; $_SESSION['forms'][$sid] = ['name' => $formName, 'expire' => time() + 3600]; self::assertTrue($this->subject->verifyFormFromQuery($formName)); self::assertFalse($this->subject->verifyFormFromQuery($formName)); } public function testATokenPostedIsNotAcceptedFromTheQueryString(): void { $formName = 'grant'; $sid = 'some-sid'; $_POST['form_validation'] = $sid; $_SESSION['forms'][$sid] = ['name' => $formName, 'expire' => time() + 3600]; self::assertFalse($this->subject->verifyFormFromQuery($formName), 'each carrier is read on its own'); } public function testGetFromPostReturnsEmptyStringIfNotContained(): void { self::assertSame( '', $this->subject->getFromPost('snafu') ); } public function testGetFromRequestReturnsEmptyStringIfNotContained(): void { self::assertSame( '', $this->subject->getFromRequest('snafu') ); } public function testVerifyFormReturnsFalseIfExpired(): void { $formName = 'some-form'; $sid = 'some-sid'; $_POST['form_validation'] = $sid; $_SESSION['forms'][$sid] = ['name' => $formName, 'expire' => time() - 1]; $this->logger->expects(static::once()) ->method('debug') ->with( sprintf('Verified SID %s for form %s', $sid, $formName), [LegacyLogger::CONTEXT_TYPE => $this->subject::class] ); $this->logger->expects(static::once()) ->method('error') ->with( sprintf('Form %s is expired, rejecting request', $formName), [LegacyLogger::CONTEXT_TYPE => $this->subject::class] ); self::assertFalse( $this->subject->verifyForm($formName) ); } public function testVerifyFormReturnsFalseIfFormNameDoesNotMatchSessionData(): void { $formName = 'some-form'; $sid = 'some-sid'; $_POST['form_validation'] = $sid; $_SESSION['forms'][$sid] = ['name' => 'lerl']; $this->logger->expects(static::once()) ->method('error') ->with( sprintf('form %s failed consistency check, rejecting request', $formName), [LegacyLogger::CONTEXT_TYPE => $this->subject::class] ); self::assertFalse( $this->subject->verifyForm($formName) ); } public function testVerifyFormReturnsFalseIfSidNotKnown(): void { $formName = 'some-form'; $this->logger->expects(static::once()) ->method('error') ->with( sprintf('Form %s not found in session, rejecting request', $formName), [LegacyLogger::CONTEXT_TYPE => $this->subject::class] ); self::assertFalse( $this->subject->verifyForm($formName) ); } public function testVerifyFormReturnsTrueIfRequestIsOk(): void { $formName = 'some-form'; $sid = 'some-sid'; $_POST['form_validation'] = $sid; $_SESSION['forms'][$sid] = ['name' => $formName, 'expire' => time() + 1000]; $this->logger->expects(static::once()) ->method('debug') ->with( sprintf('Verified SID %s for form %s', $sid, $formName), [LegacyLogger::CONTEXT_TYPE => $this->subject::class] ); self::assertTrue( $this->subject->verifyForm($formName) ); } protected function setUp(): void { $this->logger = $this->createMock(LoggerInterface::class); $this->subject = new RequestParser( $this->logger ); } }