diff --git a/site/app/Application/Theme.php b/site/app/Application/Theme.php index 89cfa377c..736a2fac2 100644 --- a/site/app/Application/Theme.php +++ b/site/app/Application/Theme.php @@ -3,23 +3,19 @@ namespace MichalSpacekCz\Application; -use MichalSpacekCz\Http\HttpInput; -use Nette\Http\IResponse; -use Nette\Http\Response; +use MichalSpacekCz\Http\Cookies\CookieName; +use MichalSpacekCz\Http\Cookies\Cookies; class Theme { - private const COOKIE = 'future'; - private const DARK = 'dark'; private const LIGHT = 'bright'; public function __construct( - private readonly HttpInput $httpInput, - private readonly IResponse $httpResponse, + private readonly Cookies $cookies, ) { } @@ -38,16 +34,20 @@ public function setLightMode(): void public function isDarkMode(): ?bool { - $cookie = $this->httpInput->getCookieString(self::COOKIE); + $cookie = $this->cookies->getString(CookieName::Theme); return $cookie === self::DARK ? true : ($cookie === self::LIGHT ? false : null); } private function setCookie(string $mode): void { - /** @var Response $response Not IResponse because https://github.com/nette/http/issues/200, can't use instanceof check because it's a different Response in tests */ - $response = $this->httpResponse; - $response->setCookie(self::COOKIE, $mode, '+10 years', null, null, null, null, 'None'); + $this->cookies->set(CookieName::Theme, $mode, $this->getCookieLifetime(), sameSite: 'None'); + } + + + public function getCookieLifetime(): string + { + return '365 days'; } } diff --git a/site/app/DateTime/DateTime.php b/site/app/DateTime/DateTime.php index 7acaa2a9c..6398f20ef 100644 --- a/site/app/DateTime/DateTime.php +++ b/site/app/DateTime/DateTime.php @@ -3,6 +3,10 @@ namespace MichalSpacekCz\DateTime; +use Exception; +use MichalSpacekCz\ShouldNotHappenException; +use Nette\Utils\DateTime as NetteDateTime; + class DateTime { @@ -11,4 +15,20 @@ class DateTime */ public const DATE_RFC3339_MICROSECONDS = 'Y-m-d\TH:i:s.uP'; + + public function getDaysFromString(string $interval): int + { + $now = new NetteDateTime(); + try { + $then = NetteDateTime::from($interval); + } catch (Exception $e) { + throw new ShouldNotHappenException("Cannot create an object from {$interval}", previous: $e); + } + $days = $now->diff($then)->days; + if ($days === false) { + throw new ShouldNotHappenException(sprintf('Cannot diff %s and %s', $now->format(DATE_RFC3339_EXTENDED), $then->format(DATE_RFC3339_EXTENDED))); + } + return $days; + } + } diff --git a/site/app/Http/Cookies/CookieDescription.php b/site/app/Http/Cookies/CookieDescription.php new file mode 100644 index 000000000..0be557340 --- /dev/null +++ b/site/app/Http/Cookies/CookieDescription.php @@ -0,0 +1,43 @@ +name; + } + + + public function isInternal(): bool + { + return $this->internal; + } + + + public function getDescription(): Html + { + return $this->description; + } + + + public function getValidDays(): ?int + { + return $this->validDays; + } + +} diff --git a/site/app/Http/Cookies/CookieDescriptions.php b/site/app/Http/Cookies/CookieDescriptions.php new file mode 100644 index 000000000..a805a9aee --- /dev/null +++ b/site/app/Http/Cookies/CookieDescriptions.php @@ -0,0 +1,72 @@ + + */ + public function get(): array + { + $options = $this->sessionHandler->getOptions(); + $cookieLifetime = $options['cookie_lifetime']; + if (!is_int($cookieLifetime)) { + throw new ShouldNotHappenException("The cookie_lifetime option should be an int, but it's a " . get_debug_type($cookieLifetime)); + } + /** @noinspection PhpInternalEntityUsedInspection */ + return [ + new CookieDescription( + CookieName::PermanentLogin->value, + true, + $this->texyFormatter->translate('messages.cookies.cookie.permanentLogin'), + $this->dateTime->getDaysFromString($this->authenticator->getPermanentLoginCookieLifetime()), + ), + new CookieDescription( + CookieName::ReturningUser->value, + true, + $this->texyFormatter->translate('messages.cookies.cookie.returningUser'), + $this->dateTime->getDaysFromString($this->authenticator->getReturningUserCookieLifetime()), + ), + new CookieDescription( + CookieName::Theme->value, + false, + $this->texyFormatter->translate('messages.cookies.cookie.theme'), + $this->dateTime->getDaysFromString($this->theme->getCookieLifetime()), + ), + new CookieDescription( + $this->sessionHandler->getName(), + false, + $this->texyFormatter->translate('messages.cookies.cookie.netteSession'), + $this->dateTime->getDaysFromString($cookieLifetime . ' seconds'), + ), + new CookieDescription( + Helpers::StrictCookieName, + false, + $this->texyFormatter->translate('messages.cookies.cookie.netteSameSiteCheck'), + null, + ), + ]; + } + +} diff --git a/site/app/Http/Cookies/CookieName.php b/site/app/Http/Cookies/CookieName.php new file mode 100644 index 000000000..23d50b333 --- /dev/null +++ b/site/app/Http/Cookies/CookieName.php @@ -0,0 +1,13 @@ +request->getCookie($name->value); + if (!is_string($cookie)) { + return null; + } + return $cookie; + } + + + public function set( + CookieName $name, + string $value, + DateTimeInterface|int|string $expire, + ?string $path = null, + ?string $domain = null, + ?bool $secure = null, + ?bool $httpOnly = null, + ?string $sameSite = null, + ): void { + /** @var Response $response Not IResponse because https://github.com/nette/http/issues/200, can't use instanceof check because it's a different Response in tests */ + $response = $this->response; + $response->setCookie($name->value, $value, $expire, $path, $domain, $secure, $httpOnly, $sameSite); + } + + + public function delete(CookieName $name, ?string $path = null, ?string $domain = null, ?bool $secure = null): void + { + $this->response->deleteCookie($name->value, $path, $domain, $secure); + } + +} diff --git a/site/app/Http/HttpInput.php b/site/app/Http/HttpInput.php index 2fc62b94b..db8bd13fe 100644 --- a/site/app/Http/HttpInput.php +++ b/site/app/Http/HttpInput.php @@ -14,16 +14,6 @@ public function __construct( } - public function getCookieString(string $key): ?string - { - $cookie = $this->request->getCookie($key); - if (!is_string($cookie)) { - return null; - } - return $cookie; - } - - public function getPostString(string $key): ?string { $data = $this->request->getPost($key); diff --git a/site/app/User/Manager.php b/site/app/User/Manager.php index 0514a4622..f9002f440 100644 --- a/site/app/User/Manager.php +++ b/site/app/User/Manager.php @@ -5,7 +5,8 @@ use DateTimeInterface; use Exception; -use MichalSpacekCz\Http\HttpInput; +use MichalSpacekCz\Http\Cookies\CookieName; +use MichalSpacekCz\Http\Cookies\Cookies; use MichalSpacekCz\User\Exceptions\IdentityException; use MichalSpacekCz\User\Exceptions\IdentityIdNotIntException; use MichalSpacekCz\User\Exceptions\IdentityNotSimpleIdentityException; @@ -16,7 +17,6 @@ use Nette\Database\Row; use Nette\Database\UniqueConstraintViolationException; use Nette\Http\IRequest; -use Nette\Http\Response; use Nette\Http\Url; use Nette\Security\AuthenticationException; use Nette\Security\Authenticator; @@ -45,13 +45,10 @@ class Manager implements Authenticator public function __construct( private readonly Explorer $database, private readonly IRequest $httpRequest, - private readonly Response $httpResponse, // Not IResponse because https://github.com/nette/http/issues/200 - private readonly HttpInput $httpInput, + private readonly Cookies $cookies, private readonly Passwords $passwords, private readonly StaticKey $passwordEncryption, LinkGenerator $linkGenerator, - private readonly string $returningUserCookie, - private readonly string $permanentLoginCookie, private readonly string $permanentLoginInterval, ) { $this->authCookiesPath = (new Url($linkGenerator->link('Admin:Sign:in')))->getPath(); @@ -190,13 +187,13 @@ public function isForbidden(): bool public function setReturningUser(string $value): void { - $this->httpResponse->setCookie($this->returningUserCookie, $value, '+10 years', $this->authCookiesPath, null, null, null, 'Strict'); + $this->cookies->set(CookieName::ReturningUser, $value, $this->getReturningUserCookieLifetime(), $this->authCookiesPath, sameSite: 'Strict'); } public function isReturningUser(): bool { - $cookie = $this->httpInput->getCookieString($this->returningUserCookie); + $cookie = $this->cookies->getString(CookieName::ReturningUser); return ($cookie && $this->verifyReturningUser($cookie)); } @@ -256,7 +253,7 @@ private function insertToken(User $user, int $type): string public function storePermanentLogin(User $user): void { $value = $this->insertToken($user, self::TOKEN_PERMANENT_LOGIN); - $this->httpResponse->setCookie($this->permanentLoginCookie, $value, $this->permanentLoginInterval, $this->authCookiesPath, null, null, null, 'Strict'); + $this->cookies->set(CookieName::PermanentLogin, $value, $this->permanentLoginInterval, $this->authCookiesPath, sameSite: 'Strict'); } @@ -268,7 +265,7 @@ public function storePermanentLogin(User $user): void public function clearPermanentLogin(User $user): void { $this->database->query('DELETE FROM auth_tokens WHERE key_user = ? AND type = ?', $user->getId(), self::TOKEN_PERMANENT_LOGIN); - $this->httpResponse->deleteCookie($this->permanentLoginCookie, $this->authCookiesPath); + $this->cookies->delete(CookieName::PermanentLogin, $this->authCookiesPath); } @@ -292,7 +289,7 @@ public function regeneratePermanentLogin(User $user): void */ public function verifyPermanentLogin(): ?Row { - $cookie = $this->httpInput->getCookieString($this->permanentLoginCookie) ?? ''; + $cookie = $this->cookies->getString(CookieName::PermanentLogin) ?? ''; return $this->verifyToken($cookie, DateTime::from("-{$this->permanentLoginInterval}"), self::TOKEN_PERMANENT_LOGIN); } @@ -354,4 +351,16 @@ private function verifyToken(string $value, DateTimeInterface $validity, int $ty return $result; } + + public function getPermanentLoginCookieLifetime(): string + { + return $this->permanentLoginInterval; + } + + + public function getReturningUserCookieLifetime(): string + { + return '365 days'; + } + } diff --git a/site/app/Www/Presenters/CookiesPresenter.php b/site/app/Www/Presenters/CookiesPresenter.php new file mode 100644 index 000000000..9bf3645cc --- /dev/null +++ b/site/app/Www/Presenters/CookiesPresenter.php @@ -0,0 +1,36 @@ +template->pageTitle = $this->translator->translate('messages.title.cookies'); + $cookies = $this->cookieDescriptions->get(); + $internalCookies = $publicCookies = []; + foreach ($cookies as $cookie) { + if ($cookie->isInternal()) { + $internalCookies[] = $cookie; + } else { + $publicCookies[] = $cookie; + } + } + $this->template->internalCookies = $internalCookies; + $this->template->publicCookies = $publicCookies; + } + +} diff --git a/site/app/Www/Presenters/templates/@layout.latte b/site/app/Www/Presenters/templates/@layout.latte index be97ecb34..087f670d4 100644 --- a/site/app/Www/Presenters/templates/@layout.latte +++ b/site/app/Www/Presenters/templates/@layout.latte @@ -70,15 +70,18 @@
diff --git a/site/app/Www/Presenters/templates/Cookies/default.latte b/site/app/Www/Presenters/templates/Cookies/default.latte new file mode 100644 index 000000000..6793a2ed8 --- /dev/null +++ b/site/app/Www/Presenters/templates/Cookies/default.latte @@ -0,0 +1,27 @@ +{varType MichalSpacekCz\Http\Cookies\CookieDescription[] $internalCookies} +{varType MichalSpacekCz\Http\Cookies\CookieDescription[] $publicCookies} +{define #menu} +» Michal Špaček +{/define} + +{define #cookies MichalSpacekCz\Http\Cookies\CookieDescription $cookie} +{$cookie->getName()}: +{$cookie->getDescription()} +{if $cookie->getValidDays()} + {_messages.cookies.expires, $cookie->getValidDays()} +{else} + {_messages.cookies.expiresEndOfSession} +{/if} +{/define} + +{define #content} +{_messages.cookies.theseAreUsed|format} +

{_messages.cookies.public}

+
    +
  1. {include #cookies $cookie}
  2. +
+

{_messages.cookies.internal}

+
    +
  1. {include #cookies $cookie}
  2. +
+{/define} diff --git a/site/app/lang/messages.cs_CZ.neon b/site/app/lang/messages.cs_CZ.neon index 53c51c625..affee15bb 100644 --- a/site/app/lang/messages.cs_CZ.neon +++ b/site/app/lang/messages.cs_CZ.neon @@ -29,6 +29,7 @@ title: projects: Projekty blog: Blog encryptedmessages: "Šifrované zprávy" + cookies: Cookies admin: talkslides: "Slajdy z přednášky %s (%s)" header: @@ -364,3 +365,15 @@ blog: duplicateslug: "Příspěvek s takovým slugem již existuje" license: content: Obsah podléhá licenci Creative Commons Uveďte původ 4.0 Mezinárodní +cookies: + theseAreUsed: "Tento web používá pouze technické //1st-party// cookies, zde je jejich seznam:" + public: "Pro všechny návštěvníky" + internal: "Navíc pro interní uživatele, což vy nejste, sorry!" + expires: "Expiruje po jednom dni.|Expiruje po %count% dnech.|Expiruje po %count% dnech." + expiresEndOfSession: "Expiruje na konci sezení." + cookie: + permanentLogin: "Obsahuje identifikátor pro permanentní přihlášení." + returningUser: "Identifikátor pro správné zobrazení přihlašovacího formuláře, bez něj se nezobrazí." + theme: "Hodnota určuje barevný režim, ve kterém se stránky zobrazí." + netteSession: "Cookie představuje identifikátor sezení, //session id//." + netteSameSiteCheck: "Používá se pro detekci //same-site// požadavků." diff --git a/site/app/lang/messages.en_US.neon b/site/app/lang/messages.en_US.neon index b368135b4..092769652 100644 --- a/site/app/lang/messages.en_US.neon +++ b/site/app/lang/messages.en_US.neon @@ -29,6 +29,7 @@ title: projects: Projects blog: Blog encryptedmessages: "Encrypted messages" + cookies: Cookies admin: talkslides: "Slides from %s (%s)" header: @@ -364,3 +365,15 @@ blog: duplicateslug: "Blog post with such slug already exists" license: content: Content licensed under a Creative Commons Attribution 4.0 International License +cookies: + theseAreUsed: "This site uses only so called technical 1st-party cookies, here's the list:" + public: "For all visitors" + internal: "Extra for internal users which you're not, sorry!" + expires: "Expires after one day.|Expires after %count% days." + expiresEndOfSession: "Expires at the end of the session." + cookie: + permanentLogin: "Contains a permanent login identifier." + returningUser: "An identifier which, if correct, will allow the login form to be displayed correctly." + theme: "The value specifies the color mode in which the pages are displayed." + netteSession: "The cookie holds the session identifier." + netteSameSiteCheck: "Used to detect //same-site// requests." diff --git a/site/config/parameters.neon b/site/config/parameters.neon index c5161753f..a7da1be1b 100644 --- a/site/config/parameters.neon +++ b/site/config/parameters.neon @@ -34,10 +34,7 @@ parameters: contentSecurityPolicy: https://plz.report-uri.com/r/default/csp/enforce vatRate: 0.21 loadCompanyDataVisible: true - returningUser: - cookie: __Secure-beenhere permanentLogin: - cookie: __Secure-permanent interval: 14 days certificatesApi: expiringThreshold: 20 diff --git a/site/config/presenters.neon b/site/config/presenters.neon index 5f59f9f6c..5f865c918 100644 --- a/site/config/presenters.neon +++ b/site/config/presenters.neon @@ -3,6 +3,7 @@ services: - MichalSpacekCz\Www\Presenters\ArticlesPresenter - MichalSpacekCz\Www\Presenters\CompanyTrainingsPresenter - MichalSpacekCz\Www\Presenters\ContactPresenter + - MichalSpacekCz\Www\Presenters\CookiesPresenter - MichalSpacekCz\Www\Presenters\ErrorPresenter - MichalSpacekCz\Www\Presenters\ErrorGenericPresenter - MichalSpacekCz\Www\Presenters\ExportsPresenter diff --git a/site/config/routes.neon b/site/config/routes.neon index e243e43c9..2ac079882 100644 --- a/site/config/routes.neon +++ b/site/config/routes.neon @@ -100,3 +100,7 @@ parameters: mask: cs_CZ: pgp en_US: pgp + Cookies: + mask: + cs_CZ: cookies + en_US: cookies diff --git a/site/config/services.neon b/site/config/services.neon index d5f1b720d..523cb9778 100644 --- a/site/config/services.neon +++ b/site/config/services.neon @@ -25,6 +25,7 @@ services: - MichalSpacekCz\CompanyInfo\CompanyRegisterAres - MichalSpacekCz\CompanyInfo\CompanyRegisterRegisterUz - MichalSpacekCz\Css\CriticalCssFactory + - MichalSpacekCz\DateTime\DateTime - MichalSpacekCz\DateTime\DateTimeFactory - MichalSpacekCz\DateTime\DateTimeFormatter(@translation.translator::getDefaultLocale()) - MichalSpacekCz\DateTime\DateTimeZoneFactory @@ -65,6 +66,8 @@ services: - MichalSpacekCz\Formatter\TexyPhraseHandler - MichalSpacekCz\Formatter\TrainingDateTexyFormatterPlaceholder httpClient: MichalSpacekCz\Http\Client\HttpClient + - MichalSpacekCz\Http\Cookies\CookieDescriptions + - MichalSpacekCz\Http\Cookies\Cookies - MichalSpacekCz\Http\HttpInput - MichalSpacekCz\Http\Redirections - MichalSpacekCz\Http\SecurityHeaders(permissionsPolicy: %permissionsPolicy%) @@ -141,7 +144,7 @@ services: - MichalSpacekCz\UpcKeys\Technicolor(@database.upcKeys.context, apiUrl: %awsLambda.upcKeys.url%, apiKey: %awsLambda.upcKeys.apiKey%) - MichalSpacekCz\UpcKeys\Ubee(@database.upcKeys.context) - MichalSpacekCz\UpcKeys\UpcKeys(routers: [@MichalSpacekCz\UpcKeys\Technicolor, @MichalSpacekCz\UpcKeys\Ubee]) - - MichalSpacekCz\User\Manager(passwordEncryption: @passwordEncryption, permanentLoginCookie: %permanentLogin.cookie%, permanentLoginInterval: %permanentLogin.interval%, returningUserCookie: %returningUser.cookie%) + - MichalSpacekCz\User\Manager(passwordEncryption: @passwordEncryption, permanentLoginInterval: %permanentLogin.interval%) - MichalSpacekCz\Utils\Strings - MichalSpacekCz\Utils\JsonUtils - Nette\Bridges\ApplicationLatte\TemplateFactory diff --git a/site/disallowed-calls.neon b/site/disallowed-calls.neon index 1bbada419..5ba2e29a5 100644 --- a/site/disallowed-calls.neon +++ b/site/disallowed-calls.neon @@ -23,15 +23,26 @@ parameters: message: 'it is not a cryptographically secure generator, use random_bytes() instead' allowIn: - tests/*.phpt + - + function: 'setcookie()' + message: 'use methods from MichalSpacekCz\Http\Cookies' disallowedMethodCalls: - method: - - 'Nette\Application\Request::getPost()' - 'Nette\Http\IRequest::getCookie()' + - 'Nette\Http\IResponse::setCookie()' + - 'Nette\Http\Response::deleteCookie()' + message: 'instead use methods from MichalSpacekCz\Http\Cookies' + allowInMethods: + - 'MichalSpacekCz\Http\Cookies\Cookies::getString()' + - 'MichalSpacekCz\Http\Cookies\Cookies::set()' + - 'MichalSpacekCz\Http\Cookies\Cookies::deleteCookie()' + - + method: + - 'Nette\Application\Request::getPost()' - 'Nette\Http\IRequest::getPost()' message: 'instead use methods from MichalSpacekCz\Http\HttpInput with more handy return types' allowInMethods: - - 'MichalSpacekCz\Http\HttpInput::getCookieString()' - 'MichalSpacekCz\Http\HttpInput::getPostString()' - 'MichalSpacekCz\Http\HttpInput::getPostArray()' - diff --git a/site/phpstan-vendor.neon b/site/phpstan-vendor.neon index 7ddd6b8b0..6bfca0e5e 100644 --- a/site/phpstan-vendor.neon +++ b/site/phpstan-vendor.neon @@ -150,7 +150,23 @@ parameters: - vendor/symfony/cache/Adapter/PhpArrayAdapter.php - vendor/symfony/filesystem/Filesystem.php - vendor/tracy/tracy/src/Tracy/Logger/FireLogger.php + - + function: 'setcookie()' + message: 'use methods from MichalSpacekCz\Http\Cookies' + allowIn: + - vendor/nette/http/src/Http/Response.php + - vendor/paragonie/halite/src/Cookie.php # Unused + - vendor/tracy/tracy/src/Tracy/Session/FileSession.php disallowedMethodCalls: + - + method: + - 'Nette\Http\IRequest::getCookie()' + - 'Nette\Http\IResponse::setCookie()' + - 'Nette\Http\Response::deleteCookie()' + message: 'instead use methods from MichalSpacekCz\Http\Cookies' + allowIn: + - vendor/nette/http/src/Http/*.php + - vendor/nette/security/src/Bridges/SecurityHttp/CookieStorage.php # Unused - method: - 'Nette\Application\Request::getPost()' diff --git a/site/psalm.xml b/site/psalm.xml index 44d1eadcc..6e4f4e185 100644 --- a/site/psalm.xml +++ b/site/psalm.xml @@ -44,6 +44,11 @@ + + + + + diff --git a/site/public/www.michalspacek.cz/i/css/screen.css b/site/public/www.michalspacek.cz/i/css/screen.css index a603182d7..208dae7da 100644 --- a/site/public/www.michalspacek.cz/i/css/screen.css +++ b/site/public/www.michalspacek.cz/i/css/screen.css @@ -139,7 +139,10 @@ button:disabled { cursor: not-allowed; } #footer .container.admin { max-width: 1180px; } #footer .container.talk { max-width: 1080px; } #footer-name { float: left; } -#footer-links { float: right; } +#footer-links { + float: right; + text-align: right; +} #header-links { position: absolute; top: 0; diff --git a/site/tests/DateTime/DateTimeTest.phpt b/site/tests/DateTime/DateTimeTest.phpt new file mode 100644 index 000000000..c52df6354 --- /dev/null +++ b/site/tests/DateTime/DateTimeTest.phpt @@ -0,0 +1,33 @@ +dateTime->getDaysFromString('303 days')); + Assert::same(14, $this->dateTime->getDaysFromString('+14 days')); + Assert::same(0, $this->dateTime->getDaysFromString('now')); + Assert::same(0, $this->dateTime->getDaysFromString('0 days')); + Assert::same(0, $this->dateTime->getDaysFromString('0')); + } + +} + +TestCaseRunner::run(DateTimeTest::class); diff --git a/site/tests/Http/Cookies/CookieDescriptionsTest.phpt b/site/tests/Http/Cookies/CookieDescriptionsTest.phpt new file mode 100644 index 000000000..c8183eeb0 --- /dev/null +++ b/site/tests/Http/Cookies/CookieDescriptionsTest.phpt @@ -0,0 +1,36 @@ + $cookieName->value, CookieName::cases()); + $expectedCookieNames[] = $this->sessionHandler->getName(); + $expectedCookieNames[] = '_nss'; + $cookieDescriptions = $this->cookieDescriptions->get(); + $describedCookieNames = array_map(fn(CookieDescription $cookieDescription): string => $cookieDescription->getName(), $cookieDescriptions); + Assert::same($expectedCookieNames, $describedCookieNames, 'All cookies must be described'); + } + +} + +TestCaseRunner::run(CookieDescriptionsTest::class); diff --git a/site/tests/Http/Cookies/CookiesTest.phpt b/site/tests/Http/Cookies/CookiesTest.phpt new file mode 100644 index 000000000..ef8488d69 --- /dev/null +++ b/site/tests/Http/Cookies/CookiesTest.phpt @@ -0,0 +1,37 @@ +cookies->getString(CookieName::Theme)); + $this->request->setCookie(CookieName::Theme->value, 'bar'); + Assert::same('bar', $this->cookies->getString(CookieName::Theme)); + PrivateProperty::setValue($this->request, 'cookies', [CookieName::ReturningUser->value => ['quux' => 'foobar']]); + Assert::null($this->cookies->getString(CookieName::ReturningUser)); + } + +} + +TestCaseRunner::run(CookiesTest::class); diff --git a/site/tests/Http/HttpInputTest.phpt b/site/tests/Http/HttpInputTest.phpt index 7d57271c1..ae7c8bd86 100644 --- a/site/tests/Http/HttpInputTest.phpt +++ b/site/tests/Http/HttpInputTest.phpt @@ -5,7 +5,6 @@ declare(strict_types = 1); namespace MichalSpacekCz\Http; use MichalSpacekCz\Test\Http\Request; -use MichalSpacekCz\Test\PrivateProperty; use MichalSpacekCz\Test\TestCaseRunner; use Tester\Assert; use Tester\TestCase; @@ -23,16 +22,6 @@ class HttpInputTest extends TestCase } - public function testGetCookieString(): void - { - Assert::null($this->httpInput->getCookieString('foo')); - $this->request->setCookie('foo', 'bar'); - Assert::same('bar', $this->httpInput->getCookieString('foo')); - PrivateProperty::setValue($this->request, 'cookies', ['waldo' => ['quux' => 'foobar']]); - Assert::null($this->httpInput->getCookieString('waldo')); - } - - public function testGetPostString(): void { Assert::null($this->httpInput->getPostString('foo'));