From 4ff1ada828f98c3f1ea567371df584684b4ec26c Mon Sep 17 00:00:00 2001 From: Fabian Schmid Date: Fri, 23 Oct 2026 22:22:32 +0200 Subject: [PATCH 1/4] [Improvement] HTTP: provide CookieFactory through the HTTP services --- .../ILIAS/DI/tests/HTTPServicesTest.php | 3 ++- components/ILIAS/HTTP/HTTP.php | 9 +++++++- components/ILIAS/HTTP/README.md | 23 +++++++++++-------- .../HTTP/src/Cookies/CookieJarWrapper.php | 1 + .../ILIAS/HTTP/src/Cookies/CookieWrapper.php | 1 + components/ILIAS/HTTP/src/GlobalHttpState.php | 6 +++++ components/ILIAS/HTTP/src/RawHTTPServices.php | 9 +++++++- components/ILIAS/HTTP/src/Services.php | 10 +++++++- .../classes/Dependencies/InitHttpServices.php | 5 ++++ .../ILIAS/WebAccessChecker/resources/wac.php | 1 + 10 files changed, 55 insertions(+), 13 deletions(-) diff --git a/components/ILIAS/DI/tests/HTTPServicesTest.php b/components/ILIAS/DI/tests/HTTPServicesTest.php index 46ab060843e8..9812775602d9 100755 --- a/components/ILIAS/DI/tests/HTTPServicesTest.php +++ b/components/ILIAS/DI/tests/HTTPServicesTest.php @@ -18,6 +18,7 @@ namespace ILIAS\DI; +use ILIAS\HTTP\Cookies\CookieFactory; use ILIAS\HTTP\Cookies\CookieJarFactory; use ILIAS\HTTP\Services; use ILIAS\HTTP\Request\RequestFactory; @@ -80,7 +81,7 @@ protected function setUp(): void $this->mockDurationFactory = $this->createMock(DurationFactory::class); - $this->httpState = new RawHTTPServices($this->mockSenderStrategy, $this->mockCookieJarFactory, $this->mockRequestFactory, $this->mockResponseFactory, $this->mockDurationFactory); + $this->httpState = new RawHTTPServices($this->mockSenderStrategy, $this->mockCookieJarFactory, $this->mockRequestFactory, $this->mockResponseFactory, $this->mockDurationFactory, $this->createMock(CookieFactory::class)); } diff --git a/components/ILIAS/HTTP/HTTP.php b/components/ILIAS/HTTP/HTTP.php index 560cdeb94b90..bb3c59f0f941 100644 --- a/components/ILIAS/HTTP/HTTP.php +++ b/components/ILIAS/HTTP/HTTP.php @@ -34,6 +34,8 @@ use ILIAS\HTTP\Duration\Increment\IncrementFactory; use ILIAS\HTTP\Response\ResponseFactory; use ILIAS\HTTP\Cookies\CookieJarFactory; +use ILIAS\HTTP\Cookies\CookieFactory; +use ILIAS\HTTP\Cookies\CookieFactoryImpl; use ILIAS\HTTP\Response\Sender\ResponseSenderStrategy; use ILIAS\HTTP\Request\HeaderSettings; use ILIAS\HTTP\Request\HeaderSettingsFromIni; @@ -59,6 +61,7 @@ public function init( $define[] = RequestFactory::class; $define[] = ResponseFactory::class; $define[] = CookieJarFactory::class; + $define[] = CookieFactory::class; $define[] = GlobalHttpState::class; $define[] = HeaderSettings::class; $define[] = HttpPathProvider::class; @@ -83,6 +86,9 @@ public function init( // RESPONSE FACTORY $implement[ResponseFactory::class] = static fn() => new ResponseFactoryImpl(); + // COOKIE FACTORY + $implement[CookieFactory::class] = static fn() => new CookieFactoryImpl(); + // COOKIE JAR FACTORY $implement[CookieJarFactory::class] = static fn() => new CookieJarFactoryImpl(); @@ -100,7 +106,8 @@ public function init( $use[ResponseFactory::class], $use[CookieJarFactory::class], $internal[ResponseSenderStrategy::class], - $internal[DurationFactory::class] + $internal[DurationFactory::class], + $use[CookieFactory::class] ); // postMessage channel for pages talking to content of another origin, see README diff --git a/components/ILIAS/HTTP/README.md b/components/ILIAS/HTTP/README.md index ce2231054662..8f8c841fc63c 100755 --- a/components/ILIAS/HTTP/README.md +++ b/components/ILIAS/HTTP/README.md @@ -124,13 +124,20 @@ and can render the newly set cookies back. Be aware that the cookie jar reads the cookies at creation time of the jar. After the new cookie header is rendered into the response the response must be saved. See the caveats section of the response for further information. + +Cookies and cookie jars are immutable as well: every `with*()` returns a new instance, which +has to be used further on. + +Cookies are created with the `CookieFactory` of the HTTP services (`$dic->http()->cookies()`, +or `$use[\ILIAS\HTTP\Cookies\CookieFactory::class]` in a component bootstrap). Components +MUST NOT instantiate the implementations (`CookieFactoryImpl`, `CookieWrapper`, ...) or use the +underlying library (`Dflydev\FigCookies\*`) directly. ### Usage #### Add new Cookie ```php http()->cookieJar(); -//create a new cookie factory -$cookieFactory = new CookieFactoryImpl(); +//get the cookie factory +$cookieFactory = $dic->http()->cookies(); //create a cookie $cookie = $cookieFactory->create("CookieName", "CookieValue"); //set the cookie path -$cookie->withPath('/'); +$cookie = $cookie->withPath('/'); //render the cookies back into the response. $response = $cookieJar @@ -163,7 +170,6 @@ $dic->http()->saveResponse($response); ```php http()->cookieJar(); -//create a new cookie factory -$cookieFactory = new CookieFactoryImpl(); +//get the cookie factory +$cookieFactory = $dic->http()->cookies(); //create an expired cookie $cookie = $cookieFactory->createExpired("CookieName"); //set the cookie path -$cookie - ->withPath('/'); +$cookie = $cookie->withPath('/'); //render the cookies back into the response. $response = $cookieJar diff --git a/components/ILIAS/HTTP/src/Cookies/CookieJarWrapper.php b/components/ILIAS/HTTP/src/Cookies/CookieJarWrapper.php index 5c3dd2edd7de..e2a730b13588 100755 --- a/components/ILIAS/HTTP/src/Cookies/CookieJarWrapper.php +++ b/components/ILIAS/HTTP/src/Cookies/CookieJarWrapper.php @@ -26,6 +26,7 @@ * * Wrapper class for the FigCookies SetCookies class. * + * @internal Create cookies with {@see CookieFactory} and cookie jars with {@see CookieJarFactory}. * @author Nicolas Schäfli * @package ILIAS\HTTP\Cookies * @since 5.3 diff --git a/components/ILIAS/HTTP/src/Cookies/CookieWrapper.php b/components/ILIAS/HTTP/src/Cookies/CookieWrapper.php index e6c9cf43e594..7aeb7097f2b3 100755 --- a/components/ILIAS/HTTP/src/Cookies/CookieWrapper.php +++ b/components/ILIAS/HTTP/src/Cookies/CookieWrapper.php @@ -24,6 +24,7 @@ /** * Class CookieWrapper * Facade class for the FigCookies SetCookie class. + * @internal Create cookies with {@see CookieFactory} and cookie jars with {@see CookieJarFactory}. * @author Nicolas Schäfli * @package ILIAS\HTTP\Cookies * @since 5.3 diff --git a/components/ILIAS/HTTP/src/GlobalHttpState.php b/components/ILIAS/HTTP/src/GlobalHttpState.php index 634f0e508327..63e12033d3d9 100755 --- a/components/ILIAS/HTTP/src/GlobalHttpState.php +++ b/components/ILIAS/HTTP/src/GlobalHttpState.php @@ -19,6 +19,7 @@ namespace ILIAS\HTTP; use ILIAS\HTTP\Cookies\CookieJar; +use ILIAS\HTTP\Cookies\CookieFactory; use ILIAS\HTTP\Response\Sender\ResponseSendingException; use ILIAS\HTTP\Wrapper\WrapperFactory; use Psr\Http\Message\RequestInterface; @@ -67,6 +68,11 @@ public function response(): ResponseInterface; */ public function cookieJar(): CookieJar; + /** + * Creates cookies, which are added to the response with {@see self::cookieJar()}. + */ + public function cookies(): CookieFactory; + /** * Saves the given request for further use. diff --git a/components/ILIAS/HTTP/src/RawHTTPServices.php b/components/ILIAS/HTTP/src/RawHTTPServices.php index 1793378afc9a..26f370a7b024 100755 --- a/components/ILIAS/HTTP/src/RawHTTPServices.php +++ b/components/ILIAS/HTTP/src/RawHTTPServices.php @@ -18,6 +18,7 @@ namespace ILIAS\HTTP; +use ILIAS\HTTP\Cookies\CookieFactory; use ILIAS\HTTP\Cookies\CookieJar; use ILIAS\HTTP\Cookies\CookieJarFactory; use ILIAS\HTTP\Request\RequestFactory; @@ -44,7 +45,8 @@ public function __construct( private CookieJarFactory $cookieJarFactory, private RequestFactory $requestFactory, private ResponseFactory $responseFactory, - private DurationFactory $durationFactory + private DurationFactory $durationFactory, + private CookieFactory $cookieFactory ) { } @@ -67,6 +69,11 @@ public function wrapper(): WrapperFactory /** * @inheritDoc */ + public function cookies(): CookieFactory + { + return $this->cookieFactory; + } + public function cookieJar(): CookieJar { return $this->cookieJarFactory->fromResponse($this->response()); diff --git a/components/ILIAS/HTTP/src/Services.php b/components/ILIAS/HTTP/src/Services.php index b8ecad08df74..e422dbcd933f 100755 --- a/components/ILIAS/HTTP/src/Services.php +++ b/components/ILIAS/HTTP/src/Services.php @@ -20,6 +20,7 @@ namespace ILIAS\HTTP; +use ILIAS\HTTP\Cookies\CookieFactory; use ILIAS\HTTP\Cookies\CookieJar; use ILIAS\HTTP\Wrapper\WrapperFactory; use Psr\Http\Message\ResponseInterface; @@ -52,13 +53,15 @@ public function __construct( CookieJarFactory $cookie_jar, ResponseSenderStrategy $response_sender_strategy, DurationFactory $duration_factory, + CookieFactory $cookie_factory, ) { $this->raw = new RawHTTPServices( $response_sender_strategy, $cookie_jar, $request_factory, $response_factory, - $duration_factory + $duration_factory, + $cookie_factory ); $this->agent = new AgentDetermination(); } @@ -112,6 +115,11 @@ public function response(): ResponseInterface * @see Services::wrapper(); * @inheritDoc */ + public function cookies(): CookieFactory + { + return $this->raw->cookies(); + } + public function cookieJar(): CookieJar { return $this->raw()->cookieJar(); diff --git a/components/ILIAS/Init/classes/Dependencies/InitHttpServices.php b/components/ILIAS/Init/classes/Dependencies/InitHttpServices.php index 04817fa83dd7..797053be8ffd 100755 --- a/components/ILIAS/Init/classes/Dependencies/InitHttpServices.php +++ b/components/ILIAS/Init/classes/Dependencies/InitHttpServices.php @@ -16,6 +16,8 @@ * *********************************************************************/ +use ILIAS\HTTP\Cookies\CookieFactoryImpl; +use ILIAS\HTTP\Cookies\CookieFactory; use ILIAS\DI\Container; use ILIAS\HTTP\Request\RequestFactoryImpl; use ILIAS\HTTP\Response\ResponseFactoryImpl; @@ -49,6 +51,8 @@ public function init(Container $container): void $container[ResponseFactory::class] = static fn($c): ResponseFactoryImpl => new ResponseFactoryImpl(); + $container[CookieFactory::class] = static fn($c): CookieFactoryImpl => new CookieFactoryImpl(); + $container[CookieJarFactory::class] = static fn($c): CookieJarFactoryImpl => new CookieJarFactoryImpl(); $container[ResponseSenderStrategy::class] = static fn( @@ -69,6 +73,7 @@ public function init(Container $container): void $c[CookieJarFactory::class], $c[ResponseSenderStrategy::class], $c[DurationFactory::class], + $c[CookieFactory::class], ); $container['http'] = static fn($c): Services => $c[GlobalHttpState::class]; } diff --git a/components/ILIAS/WebAccessChecker/resources/wac.php b/components/ILIAS/WebAccessChecker/resources/wac.php index ba4ef3cfb5e6..2f021eb6a88c 100755 --- a/components/ILIAS/WebAccessChecker/resources/wac.php +++ b/components/ILIAS/WebAccessChecker/resources/wac.php @@ -58,6 +58,7 @@ $c['http.cookie_jar_factory'], $c['http.response_sender_strategy'], $c['http.duration_factory'], + new CookieFactoryImpl(), ); $GLOBALS["DIC"] = $container; From 0e4d3d37815ad72e5cc40500c8dabb8e53cc2635 Mon Sep 17 00:00:00 2001 From: Fabian Schmid Date: Fri, 23 Oct 2026 22:22:32 +0200 Subject: [PATCH 2/4] [Improvement] use the CookieFactory of the HTTP services instead of its implementation --- components/ILIAS/Utilities/classes/class.ilUtil.php | 3 +-- .../WebAccessChecker/classes/class.ilWACSignedPath.php | 5 ++--- components/ILIAS/WebAccessChecker/resources/wac.php | 2 +- .../WebAccessChecker/tests/Token/ilWACTokenTestTBD.php | 8 +++----- 4 files changed, 7 insertions(+), 11 deletions(-) diff --git a/components/ILIAS/Utilities/classes/class.ilUtil.php b/components/ILIAS/Utilities/classes/class.ilUtil.php index 477e99d9edd4..77e0494bdf16 100755 --- a/components/ILIAS/Utilities/classes/class.ilUtil.php +++ b/components/ILIAS/Utilities/classes/class.ilUtil.php @@ -18,7 +18,6 @@ use ILIAS\FileDelivery\Delivery; use ILIAS\Filesystem\Stream\Streams; -use ILIAS\HTTP\Cookies\CookieFactoryImpl; use ILIAS\HTTP\Cookies\Cookie; /** @@ -1289,7 +1288,7 @@ public static function setCookie( $http = $DIC->http(); $cookie_jar = $http->cookieJar(); - $cookie_factory = new CookieFactoryImpl(); + $cookie_factory = $http->cookies(); $cookie_expire = null; if (defined('IL_COOKIE_EXPIRE') && is_numeric(IL_COOKIE_EXPIRE) && IL_COOKIE_EXPIRE > 0) { diff --git a/components/ILIAS/WebAccessChecker/classes/class.ilWACSignedPath.php b/components/ILIAS/WebAccessChecker/classes/class.ilWACSignedPath.php index ef681a557448..3e5bd0bcac0e 100755 --- a/components/ILIAS/WebAccessChecker/classes/class.ilWACSignedPath.php +++ b/components/ILIAS/WebAccessChecker/classes/class.ilWACSignedPath.php @@ -18,7 +18,6 @@ // declare(strict_types=1); use ILIAS\HTTP\Cookies\CookieFactory; -use ILIAS\HTTP\Cookies\CookieFactoryImpl; use ILIAS\WebAccessChecker\PathType; use ILIAS\HTTP\GlobalHttpState; @@ -219,7 +218,7 @@ public static function signFile(string $path_to_file): string if ($ilWACPath->getClient() === '' || $ilWACPath->getClient() === '0') { return $path_to_file; } - $obj = new self($ilWACPath, $DIC->http(), new CookieFactoryImpl()); + $obj = new self($ilWACPath, $DIC->http(), $DIC->http()->cookies()); $obj->setType(PathType::FILE); $obj->buildAndSetTokenInstance(time(), self::getTokenMaxLifetimeInSeconds()); @@ -229,7 +228,7 @@ public static function signFile(string $path_to_file): string public static function signFolderOfStartFile(string $start_file_path): void { global $DIC; - $obj = new self(new ilWACPath($start_file_path), $DIC->http(), new CookieFactoryImpl()); + $obj = new self(new ilWACPath($start_file_path), $DIC->http(), $DIC->http()->cookies()); $obj->setType(PathType::FOLDER); $obj->buildAndSetTokenInstance(time(), self::getCookieMaxLifetimeInSeconds()); $obj->saveFolderToken(); diff --git a/components/ILIAS/WebAccessChecker/resources/wac.php b/components/ILIAS/WebAccessChecker/resources/wac.php index 2f021eb6a88c..31eec7073039 100755 --- a/components/ILIAS/WebAccessChecker/resources/wac.php +++ b/components/ILIAS/WebAccessChecker/resources/wac.php @@ -69,7 +69,7 @@ $Services = $container['http']; //TODO: fix tests and mod_xsendfile which refuses to work -ilWebAccessCheckerDelivery::run($Services, new CookieFactoryImpl()); +ilWebAccessCheckerDelivery::run($Services, $Services->cookies()); //send response $Services->sendResponse(); diff --git a/components/ILIAS/WebAccessChecker/tests/Token/ilWACTokenTestTBD.php b/components/ILIAS/WebAccessChecker/tests/Token/ilWACTokenTestTBD.php index 0699ec5ace54..7e4d850b059e 100644 --- a/components/ILIAS/WebAccessChecker/tests/Token/ilWACTokenTestTBD.php +++ b/components/ILIAS/WebAccessChecker/tests/Token/ilWACTokenTestTBD.php @@ -29,8 +29,6 @@ use Mockery\MockInterface; use org\bovigo\vfs; use Psr\Http\Message\ResponseInterface; -use ILIAS\HTTP\Cookies\CookieWrapper; -use Dflydev\FigCookies\SetCookie; /** * TestCase for the ilWACTokenTest @@ -216,19 +214,19 @@ public function testCookieGeneration(): void $cookieJar ->shouldReceive('with') ->times(3) - ->with(new CookieWrapper(SetCookie::create(''))) + ->with((new CookieFactoryImpl())->create('')) ->andReturnSelf() ->getMock() ->shouldReceive('with') ->times(3) - ->with(new CookieWrapper(SetCookie::create(''))) + ->with((new CookieFactoryImpl())->create('')) ->andReturnSelf() ->getMock() ->shouldReceive('with') ->times(3) - ->with(new CookieWrapper(SetCookie::create(''))) + ->with((new CookieFactoryImpl())->create('')) ->andReturnSelf() ->getMock(); From 37d0d4e4b3eeb2beb467612c9249a2cc01376d19 Mon Sep 17 00:00:00 2001 From: Fabian Schmid Date: Fri, 23 Oct 2026 22:22:32 +0200 Subject: [PATCH 3/4] [Improvement] GlobalScreen: remove unused ModeToggle --- .../GlobalScreen/src/Client/ModeToggle.php | 78 ------------------- 1 file changed, 78 deletions(-) delete mode 100755 components/ILIAS/GlobalScreen/src/Client/ModeToggle.php diff --git a/components/ILIAS/GlobalScreen/src/Client/ModeToggle.php b/components/ILIAS/GlobalScreen/src/Client/ModeToggle.php deleted file mode 100755 index 5263133d4453..000000000000 --- a/components/ILIAS/GlobalScreen/src/Client/ModeToggle.php +++ /dev/null @@ -1,78 +0,0 @@ - - */ -class ModeToggle -{ - public const GS_MODE = 'gs_mode'; - public const MODE1 = "all"; - public const MODE2 = "none"; - - protected WrapperFactory $wrapper; - protected Factory $refinery; - protected \ilCtrlInterface $ctrl; - protected Services $global_screen; - protected \ILIAS\HTTP\Services $http; - - public function __construct() - { - \ilInitialisation::initILIAS(); - global $DIC; - $this->ctrl = $DIC->ctrl(); - $this->wrapper = $DIC->http()->wrapper(); - $this->http = $DIC->http(); - $this->refinery = $DIC->refinery(); - $this->global_screen = $DIC->globalScreen(); - } - - public function getMode(): string - { - return $this->wrapper->cookie()->has(self::GS_MODE) - ? $this->wrapper->cookie()->retrieve(self::GS_MODE, $this->refinery->to()->string()) - : self::MODE1; - } - - public function saveStateOfAll(): bool - { - return $this->getMode() == ItemState::LEVEL_OF_TOOL; - } - - public function toggle(): void - { - $current_mode = $this->getMode(); - $new_mode = $current_mode === self::MODE1 ? self::MODE2 : self::MODE1; - $cookie_factory = new CookieFactoryImpl(); - $cookie = $cookie_factory->create(self::GS_MODE, $new_mode) - ->withExpires(time() + 3600); - $this->http->cookieJar()->with($cookie); - header('Location: ' . $_SERVER['HTTP_REFERER']); - } -} From c62b38340bdb7e93e0bf0c957994cfa294dee343 Mon Sep 17 00:00:00 2001 From: Fabian Schmid Date: Fri, 23 Oct 2026 22:38:36 +0200 Subject: [PATCH 4/4] [Improvement] User: remove WAC secure path for usr_images --- components/ILIAS/User/service.xml | 3 --- 1 file changed, 3 deletions(-) diff --git a/components/ILIAS/User/service.xml b/components/ILIAS/User/service.xml index 9067985c7b64..510143f26059 100755 --- a/components/ILIAS/User/service.xml +++ b/components/ILIAS/User/service.xml @@ -25,8 +25,5 @@ - - -