diff options
Diffstat (limited to 'tests/lib/AppFramework')
66 files changed, 4905 insertions, 4006 deletions
diff --git a/tests/lib/AppFramework/AppTest.php b/tests/lib/AppFramework/AppTest.php index 3dc62246e97..f9b7cf50675 100644 --- a/tests/lib/AppFramework/AppTest.php +++ b/tests/lib/AppFramework/AppTest.php @@ -1,32 +1,18 @@ <?php /** - * ownCloud - App Framework - * - * @author Bernhard Posselt - * @copyright 2012 Bernhard Posselt <dev@bernhard-posselt.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\AppFramework; use OC\AppFramework\App; +use OC\AppFramework\DependencyInjection\DIContainer; use OC\AppFramework\Http\Dispatcher; use OCP\AppFramework\Controller; -use OCP\AppFramework\Http; +use OCP\AppFramework\Http\IOutput; use OCP\AppFramework\Http\Response; function rrmdir($directory) { @@ -43,7 +29,7 @@ function rrmdir($directory) { class AppTest extends \Test\TestCase { - private $container; + private DIContainer $container; private $io; private $api; private $controller; @@ -58,10 +44,10 @@ class AppTest extends \Test\TestCase { protected function setUp(): void { parent::setUp(); - $this->container = new \OC\AppFramework\DependencyInjection\DIContainer('test', []); + $this->container = new DIContainer('test', []); $this->controller = $this->createMock(Controller::class); $this->dispatcher = $this->createMock(Dispatcher::class); - $this->io = $this->createMock(Http\IOutput::class); + $this->io = $this->createMock(IOutput::class); $this->headers = ['key' => 'value']; $this->output = 'hi'; @@ -69,24 +55,24 @@ class AppTest extends \Test\TestCase { $this->controllerMethod = 'method'; $this->container[$this->controllerName] = $this->controller; - $this->container['Dispatcher'] = $this->dispatcher; - $this->container['OCP\\AppFramework\\Http\\IOutput'] = $this->io; + $this->container[Dispatcher::class] = $this->dispatcher; + $this->container[IOutput::class] = $this->io; $this->container['urlParams'] = ['_route' => 'not-profiler']; $this->appPath = __DIR__ . '/../../../apps/namespacetestapp'; $infoXmlPath = $this->appPath . '/appinfo/info.xml'; mkdir($this->appPath . '/appinfo', 0777, true); - $xml = '<?xml version="1.0" encoding="UTF-8"?>' . - '<info>' . - '<id>namespacetestapp</id>' . - '<namespace>NameSpaceTestApp</namespace>' . - '</info>'; + $xml = '<?xml version="1.0" encoding="UTF-8"?>' + . '<info>' + . '<id>namespacetestapp</id>' + . '<namespace>NameSpaceTestApp</namespace>' + . '</info>'; file_put_contents($infoXmlPath, $xml); } - public function testControllerNameAndMethodAreBeingPassed() { + public function testControllerNameAndMethodAreBeingPassed(): void { $return = ['HTTP/2.0 200 OK', [], [], null, new Response()]; $this->dispatcher->expects($this->once()) ->method('dispatch') @@ -102,19 +88,19 @@ class AppTest extends \Test\TestCase { } - public function testBuildAppNamespace() { + public function testBuildAppNamespace(): void { $ns = App::buildAppNamespace('someapp'); $this->assertEquals('OCA\Someapp', $ns); } - public function testBuildAppNamespaceCore() { + public function testBuildAppNamespaceCore(): void { $ns = App::buildAppNamespace('someapp', 'OC\\'); $this->assertEquals('OC\Someapp', $ns); } - public function testBuildAppNamespaceInfoXml() { + public function testBuildAppNamespaceInfoXml(): void { $ns = App::buildAppNamespace('namespacetestapp', 'OCA\\'); $this->assertEquals('OCA\NameSpaceTestApp', $ns); } @@ -126,7 +112,7 @@ class AppTest extends \Test\TestCase { } - public function testOutputIsPrinted() { + public function testOutputIsPrinted(): void { $return = ['HTTP/2.0 200 OK', [], [], $this->output, new Response()]; $this->dispatcher->expects($this->once()) ->method('dispatch') @@ -139,17 +125,15 @@ class AppTest extends \Test\TestCase { App::main($this->controllerName, $this->controllerMethod, $this->container, []); } - public function dataNoOutput() { + public static function dataNoOutput(): array { return [ ['HTTP/2.0 204 No content'], ['HTTP/2.0 304 Not modified'], ]; } - /** - * @dataProvider dataNoOutput - */ - public function testNoOutput(string $statusCode) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataNoOutput')] + public function testNoOutput(string $statusCode): void { $return = [$statusCode, [], [], $this->output, new Response()]; $this->dispatcher->expects($this->once()) ->method('dispatch') @@ -165,7 +149,7 @@ class AppTest extends \Test\TestCase { } - public function testCallbackIsCalled() { + public function testCallbackIsCalled(): void { $mock = $this->getMockBuilder('OCP\AppFramework\Http\ICallbackResponse') ->getMock(); @@ -180,8 +164,8 @@ class AppTest extends \Test\TestCase { App::main($this->controllerName, $this->controllerMethod, $this->container, []); } - public function testCoreApp() { - $this->container['AppName'] = 'core'; + public function testCoreApp(): void { + $this->container['appName'] = 'core'; $this->container['OC\Core\Controller\Foo'] = $this->controller; $this->container['urlParams'] = ['_route' => 'not-profiler']; @@ -198,8 +182,8 @@ class AppTest extends \Test\TestCase { App::main('Foo', $this->controllerMethod, $this->container); } - public function testSettingsApp() { - $this->container['AppName'] = 'settings'; + public function testSettingsApp(): void { + $this->container['appName'] = 'settings'; $this->container['OCA\Settings\Controller\Foo'] = $this->controller; $this->container['urlParams'] = ['_route' => 'not-profiler']; @@ -216,8 +200,8 @@ class AppTest extends \Test\TestCase { App::main('Foo', $this->controllerMethod, $this->container); } - public function testApp() { - $this->container['AppName'] = 'bar'; + public function testApp(): void { + $this->container['appName'] = 'bar'; $this->container['OCA\Bar\Controller\Foo'] = $this->controller; $this->container['urlParams'] = ['_route' => 'not-profiler']; diff --git a/tests/lib/AppFramework/Bootstrap/BootContextTest.php b/tests/lib/AppFramework/Bootstrap/BootContextTest.php index b566347e3ff..3a97ff2bbfc 100644 --- a/tests/lib/AppFramework/Bootstrap/BootContextTest.php +++ b/tests/lib/AppFramework/Bootstrap/BootContextTest.php @@ -3,24 +3,8 @@ declare(strict_types=1); /** - * @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author 2020 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace lib\AppFramework\Bootstrap; @@ -32,7 +16,6 @@ use PHPUnit\Framework\MockObject\MockObject; use Test\TestCase; class BootContextTest extends TestCase { - /** @var IAppContainer|MockObject */ private $appContainer; diff --git a/tests/lib/AppFramework/Bootstrap/CoordinatorTest.php b/tests/lib/AppFramework/Bootstrap/CoordinatorTest.php index 5a151806adf..0eeddb2173a 100644 --- a/tests/lib/AppFramework/Bootstrap/CoordinatorTest.php +++ b/tests/lib/AppFramework/Bootstrap/CoordinatorTest.php @@ -3,30 +3,15 @@ declare(strict_types=1); /** - * @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author 2020 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace lib\AppFramework\Bootstrap; use OC\AppFramework\Bootstrap\Coordinator; use OC\Support\CrashReport\Registry; +use OCA\Settings\AppInfo\Application; use OCP\App\IAppManager; use OCP\AppFramework\App; use OCP\AppFramework\Bootstrap\IBootContext; @@ -42,7 +27,6 @@ use Psr\Log\LoggerInterface; use Test\TestCase; class CoordinatorTest extends TestCase { - /** @var IAppManager|MockObject */ private $appManager; @@ -84,7 +68,8 @@ class CoordinatorTest extends TestCase { $this->dashboardManager, $this->eventDispatcher, $this->eventLogger, - $this->logger + $this->appManager, + $this->logger, ); } @@ -92,8 +77,8 @@ class CoordinatorTest extends TestCase { $appId = 'settings'; $this->serverContainer->expects($this->once()) ->method('query') - ->with(\OCA\Settings\AppInfo\Application::class) - ->willThrowException(new QueryException("")); + ->with(Application::class) + ->willThrowException(new QueryException('')); $this->logger->expects($this->once()) ->method('error'); @@ -102,10 +87,10 @@ class CoordinatorTest extends TestCase { public function testBootAppNotBootable(): void { $appId = 'settings'; - $mockApp = $this->createMock(\OCA\Settings\AppInfo\Application::class); + $mockApp = $this->createMock(Application::class); $this->serverContainer->expects($this->once()) ->method('query') - ->with(\OCA\Settings\AppInfo\Application::class) + ->with(Application::class) ->willReturn($mockApp); $this->coordinator->bootApp($appId); @@ -126,7 +111,7 @@ class CoordinatorTest extends TestCase { }; $this->serverContainer->expects($this->once()) ->method('query') - ->with(\OCA\Settings\AppInfo\Application::class) + ->with(Application::class) ->willReturn($mockApp); $this->coordinator->bootApp($appId); diff --git a/tests/lib/AppFramework/Bootstrap/FunctionInjectorTest.php b/tests/lib/AppFramework/Bootstrap/FunctionInjectorTest.php index cd2332b0588..8f6944ce34f 100644 --- a/tests/lib/AppFramework/Bootstrap/FunctionInjectorTest.php +++ b/tests/lib/AppFramework/Bootstrap/FunctionInjectorTest.php @@ -3,37 +3,21 @@ declare(strict_types=1); /** - * @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author 2020 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace lib\AppFramework\Bootstrap; use OC\AppFramework\Bootstrap\FunctionInjector; use OC\AppFramework\Utility\SimpleContainer; +use OCP\AppFramework\QueryException; use Test\TestCase; interface Foo { } class FunctionInjectorTest extends TestCase { - /** @var SimpleContainer */ private $container; @@ -44,7 +28,7 @@ class FunctionInjectorTest extends TestCase { } public function testInjectFnNotRegistered(): void { - $this->expectException(\OCP\AppFramework\QueryException::class); + $this->expectException(QueryException::class); (new FunctionInjector($this->container))->injectFn(static function (Foo $p1): void { }); diff --git a/tests/lib/AppFramework/Bootstrap/RegistrationContextTest.php b/tests/lib/AppFramework/Bootstrap/RegistrationContextTest.php index 81fac49348b..c0095713370 100644 --- a/tests/lib/AppFramework/Bootstrap/RegistrationContextTest.php +++ b/tests/lib/AppFramework/Bootstrap/RegistrationContextTest.php @@ -3,30 +3,15 @@ declare(strict_types=1); /** - * @copyright 2020 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author 2020 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace lib\AppFramework\Bootstrap; use OC\AppFramework\Bootstrap\RegistrationContext; use OC\AppFramework\Bootstrap\ServiceRegistration; +use OC\Core\Middleware\TwoFactorMiddleware; use OCP\AppFramework\App; use OCP\AppFramework\IAppContainer; use OCP\EventDispatcher\IEventDispatcher; @@ -35,7 +20,6 @@ use Psr\Log\LoggerInterface; use Test\TestCase; class RegistrationContextTest extends TestCase { - /** @var LoggerInterface|MockObject */ private $logger; @@ -84,9 +68,7 @@ class RegistrationContextTest extends TestCase { $this->context->delegateEventListenerRegistrations($dispatcher); } - /** - * @dataProvider dataProvider_TrueFalse - */ + #[\PHPUnit\Framework\Attributes\DataProvider('dataProvider_TrueFalse')] public function testRegisterService(bool $shared): void { $app = $this->createMock(App::class); $service = 'abc'; @@ -146,24 +128,6 @@ class RegistrationContextTest extends TestCase { ]); } - public function testRegisterMiddleware(): void { - $app = $this->createMock(App::class); - $name = 'abc'; - $container = $this->createMock(IAppContainer::class); - $app->method('getContainer') - ->willReturn($container); - $container->expects($this->once()) - ->method('registerMiddleware') - ->with($name); - $this->logger->expects($this->never()) - ->method('error'); - - $this->context->for('myapp')->registerMiddleware($name); - $this->context->delegateMiddlewareRegistrations([ - 'myapp' => $app, - ]); - } - public function testRegisterUserMigrator(): void { $appIdA = 'myapp'; $migratorClassA = 'OCA\App\UserMigration\AppMigrator'; @@ -190,10 +154,20 @@ class RegistrationContextTest extends TestCase { ); } - public function dataProvider_TrueFalse() { + public static function dataProvider_TrueFalse(): array { return[ [true], [false] ]; } + + public function testGetMiddlewareRegistrations(): void { + $this->context->registerMiddleware('core', TwoFactorMiddleware::class, false); + + $registrations = $this->context->getMiddlewareRegistrations(); + + self::assertNotEmpty($registrations); + self::assertSame('core', $registrations[0]->getAppId()); + self::assertSame(TwoFactorMiddleware::class, $registrations[0]->getService()); + } } diff --git a/tests/lib/AppFramework/Controller/ApiControllerTest.php b/tests/lib/AppFramework/Controller/ApiControllerTest.php index 975b92c5b96..9dd980f975f 100644 --- a/tests/lib/AppFramework/Controller/ApiControllerTest.php +++ b/tests/lib/AppFramework/Controller/ApiControllerTest.php @@ -1,24 +1,9 @@ <?php /** - * ownCloud - App Framework - * - * @author Bernhard Posselt - * @copyright 2012 Bernhard Posselt <dev@bernhard-posselt.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\AppFramework\Controller; @@ -36,7 +21,7 @@ class ApiControllerTest extends \Test\TestCase { /** @var ChildApiController */ protected $controller; - public function testCors() { + public function testCors(): void { $request = new Request( ['server' => ['HTTP_ORIGIN' => 'test']], $this->createMock(IRequestId::class), diff --git a/tests/lib/AppFramework/Controller/AuthPublicShareControllerTest.php b/tests/lib/AppFramework/Controller/AuthPublicShareControllerTest.php index c8adef80966..4efcac2dccf 100644 --- a/tests/lib/AppFramework/Controller/AuthPublicShareControllerTest.php +++ b/tests/lib/AppFramework/Controller/AuthPublicShareControllerTest.php @@ -1,24 +1,8 @@ <?php + /** - * @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\AppFramework\Controller; @@ -31,7 +15,6 @@ use OCP\ISession; use OCP\IURLGenerator; class AuthPublicShareControllerTest extends \Test\TestCase { - /** @var IRequest|\PHPUnit\Framework\MockObject\MockObject */ private $request; /** @var ISession|\PHPUnit\Framework\MockObject\MockObject */ @@ -56,7 +39,7 @@ class AuthPublicShareControllerTest extends \Test\TestCase { $this->request, $this->session, $this->urlGenerator - ])->setMethods([ + ])->onlyMethods([ 'authFailed', 'getPasswordHash', 'isAuthenticated', @@ -69,20 +52,22 @@ class AuthPublicShareControllerTest extends \Test\TestCase { ])->getMock(); } - public function testShowAuthenticate() { + public function testShowAuthenticate(): void { $expects = new TemplateResponse('core', 'publicshareauth', [], 'guest'); $this->assertEquals($expects, $this->controller->showAuthenticate()); } - public function testAuthenticateAuthenticated() { + public function testAuthenticateAuthenticated(): void { $this->controller->method('isAuthenticated') ->willReturn(true); $this->controller->setToken('myToken'); $this->session->method('get') - ->willReturnMap(['public_link_authenticate_redirect', ['foo' => 'bar']]); + ->willReturnMap([ + ['public_link_authenticate_redirect', json_encode(['foo' => 'bar'])], + ]); $this->urlGenerator->method('linkToRoute') ->willReturn('myLink!'); @@ -92,7 +77,7 @@ class AuthPublicShareControllerTest extends \Test\TestCase { $this->assertSame('myLink!', $result->getRedirectURL()); } - public function testAuthenticateInvalidPassword() { + public function testAuthenticateInvalidPassword(): void { $this->controller->setToken('token'); $this->controller->method('isPasswordProtected') ->willReturn(true); @@ -112,7 +97,7 @@ class AuthPublicShareControllerTest extends \Test\TestCase { $this->assertEquals($expects, $result); } - public function testAuthenticateValidPassword() { + public function testAuthenticateValidPassword(): void { $this->controller->setToken('token'); $this->controller->method('isPasswordProtected') ->willReturn(true); @@ -125,7 +110,9 @@ class AuthPublicShareControllerTest extends \Test\TestCase { $this->session->expects($this->once()) ->method('regenerateId'); $this->session->method('get') - ->willReturnMap(['public_link_authenticate_redirect', ['foo' => 'bar']]); + ->willReturnMap([ + ['public_link_authenticate_redirect', json_encode(['foo' => 'bar'])], + ]); $tokenSet = false; $hashSet = false; diff --git a/tests/lib/AppFramework/Controller/ControllerTest.php b/tests/lib/AppFramework/Controller/ControllerTest.php index 4d36fcadce1..aa016872847 100644 --- a/tests/lib/AppFramework/Controller/ControllerTest.php +++ b/tests/lib/AppFramework/Controller/ControllerTest.php @@ -1,28 +1,14 @@ <?php /** - * ownCloud - App Framework - * - * @author Bernhard Posselt - * @copyright 2012 Bernhard Posselt <dev@bernhard-posselt.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\AppFramework\Controller; +use OC\AppFramework\DependencyInjection\DIContainer; use OC\AppFramework\Http\Request; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\DataResponse; @@ -30,7 +16,6 @@ use OCP\AppFramework\Http\JSONResponse; use OCP\IConfig; use OCP\IRequest; use OCP\IRequestId; -use OC\AppFramework\DependencyInjection\DIContainer; class ChildController extends Controller { public function __construct($appName, $request) { @@ -56,7 +41,6 @@ class ChildController extends Controller { }; class ControllerTest extends \Test\TestCase { - /** * @var Controller */ @@ -82,12 +66,12 @@ class ControllerTest extends \Test\TestCase { ); $this->app = $this->getMockBuilder(DIContainer::class) - ->setMethods(['getAppName']) + ->onlyMethods(['getAppName']) ->setConstructorArgs(['test']) ->getMock(); $this->app->expects($this->any()) - ->method('getAppName') - ->willReturn('apptemplate_advanced'); + ->method('getAppName') + ->willReturn('apptemplate_advanced'); $this->controller = new ChildController($this->app, $request); $this->overwriteService(IRequest::class, $request); @@ -95,21 +79,21 @@ class ControllerTest extends \Test\TestCase { } - public function testFormatResonseInvalidFormat() { + public function testFormatResonseInvalidFormat(): void { $this->expectException(\DomainException::class); $this->controller->buildResponse(null, 'test'); } - public function testFormat() { + public function testFormat(): void { $response = $this->controller->buildResponse(['hi'], 'json'); $this->assertEquals(['hi'], $response->getData()); } - public function testFormatDataResponseJSON() { + public function testFormatDataResponseJSON(): void { $expectedHeaders = [ 'test' => 'something', 'Cache-Control' => 'no-cache, no-store, must-revalidate', @@ -117,7 +101,7 @@ class ControllerTest extends \Test\TestCase { 'Content-Security-Policy' => "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'", 'Feature-Policy' => "autoplay 'none';camera 'none';fullscreen 'none';geolocation 'none';microphone 'none';payment 'none'", 'X-Request-Id' => $this->request->getId(), - 'X-Robots-Tag' => 'none', + 'X-Robots-Tag' => 'noindex, nofollow', ]; $response = $this->controller->customDataResponse(['hi']); @@ -129,7 +113,7 @@ class ControllerTest extends \Test\TestCase { } - public function testCustomFormatter() { + public function testCustomFormatter(): void { $response = $this->controller->custom('hi'); $response = $this->controller->buildResponse($response, 'json'); @@ -137,14 +121,14 @@ class ControllerTest extends \Test\TestCase { } - public function testDefaultResponderToJSON() { + public function testDefaultResponderToJSON(): void { $responder = $this->controller->getResponderByHTTPHeader('*/*'); $this->assertEquals('json', $responder); } - public function testResponderAcceptHeaderParsed() { + public function testResponderAcceptHeaderParsed(): void { $responder = $this->controller->getResponderByHTTPHeader( '*/*, application/tom, application/json' ); @@ -153,7 +137,7 @@ class ControllerTest extends \Test\TestCase { } - public function testResponderAcceptHeaderParsedUpperCase() { + public function testResponderAcceptHeaderParsedUpperCase(): void { $responder = $this->controller->getResponderByHTTPHeader( '*/*, apPlication/ToM, application/json' ); diff --git a/tests/lib/AppFramework/Controller/OCSControllerTest.php b/tests/lib/AppFramework/Controller/OCSControllerTest.php index ce110f435ef..4ab45ad6b06 100644 --- a/tests/lib/AppFramework/Controller/OCSControllerTest.php +++ b/tests/lib/AppFramework/Controller/OCSControllerTest.php @@ -1,24 +1,9 @@ <?php /** - * ownCloud - App Framework - * - * @author Bernhard Posselt - * @copyright 2015 Bernhard Posselt <dev@bernhard-posselt.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\AppFramework\Controller; @@ -35,7 +20,7 @@ class ChildOCSController extends OCSController { class OCSControllerTest extends \Test\TestCase { - public function testCors() { + public function testCors(): void { $request = new Request( [ 'server' => [ @@ -60,7 +45,7 @@ class OCSControllerTest extends \Test\TestCase { } - public function testXML() { + public function testXML(): void { $controller = new ChildOCSController('app', new Request( [], $this->createMock(IRequestId::class), @@ -68,19 +53,19 @@ class OCSControllerTest extends \Test\TestCase { )); $controller->setOCSVersion(1); - $expected = "<?xml version=\"1.0\"?>\n" . - "<ocs>\n" . - " <meta>\n" . - " <status>ok</status>\n" . - " <statuscode>100</statuscode>\n" . - " <message>OK</message>\n" . - " <totalitems></totalitems>\n" . - " <itemsperpage></itemsperpage>\n" . - " </meta>\n" . - " <data>\n" . - " <test>hi</test>\n" . - " </data>\n" . - "</ocs>\n"; + $expected = "<?xml version=\"1.0\"?>\n" + . "<ocs>\n" + . " <meta>\n" + . " <status>ok</status>\n" + . " <statuscode>100</statuscode>\n" + . " <message>OK</message>\n" + . " <totalitems></totalitems>\n" + . " <itemsperpage></itemsperpage>\n" + . " </meta>\n" + . " <data>\n" + . " <test>hi</test>\n" + . " </data>\n" + . "</ocs>\n"; $params = new DataResponse(['test' => 'hi']); @@ -89,15 +74,15 @@ class OCSControllerTest extends \Test\TestCase { $this->assertEquals($expected, $response->render()); } - public function testJSON() { + public function testJSON(): void { $controller = new ChildOCSController('app', new Request( [], $this->createMock(IRequestId::class), $this->createMock(IConfig::class) )); $controller->setOCSVersion(1); - $expected = '{"ocs":{"meta":{"status":"ok","statuscode":100,"message":"OK",' . - '"totalitems":"","itemsperpage":""},"data":{"test":"hi"}}}'; + $expected = '{"ocs":{"meta":{"status":"ok","statuscode":100,"message":"OK",' + . '"totalitems":"","itemsperpage":""},"data":{"test":"hi"}}}'; $params = new DataResponse(['test' => 'hi']); $response = $controller->buildResponse($params, 'json'); @@ -106,7 +91,7 @@ class OCSControllerTest extends \Test\TestCase { $this->assertEquals($expected, $response->render()); } - public function testXMLV2() { + public function testXMLV2(): void { $controller = new ChildOCSController('app', new Request( [], $this->createMock(IRequestId::class), @@ -114,17 +99,17 @@ class OCSControllerTest extends \Test\TestCase { )); $controller->setOCSVersion(2); - $expected = "<?xml version=\"1.0\"?>\n" . - "<ocs>\n" . - " <meta>\n" . - " <status>ok</status>\n" . - " <statuscode>200</statuscode>\n" . - " <message>OK</message>\n" . - " </meta>\n" . - " <data>\n" . - " <test>hi</test>\n" . - " </data>\n" . - "</ocs>\n"; + $expected = "<?xml version=\"1.0\"?>\n" + . "<ocs>\n" + . " <meta>\n" + . " <status>ok</status>\n" + . " <statuscode>200</statuscode>\n" + . " <message>OK</message>\n" + . " </meta>\n" + . " <data>\n" + . " <test>hi</test>\n" + . " </data>\n" + . "</ocs>\n"; $params = new DataResponse(['test' => 'hi']); @@ -133,7 +118,7 @@ class OCSControllerTest extends \Test\TestCase { $this->assertEquals($expected, $response->render()); } - public function testJSONV2() { + public function testJSONV2(): void { $controller = new ChildOCSController('app', new Request( [], $this->createMock(IRequestId::class), diff --git a/tests/lib/AppFramework/Controller/PublicShareControllerTest.php b/tests/lib/AppFramework/Controller/PublicShareControllerTest.php index deffedcec05..e676b8a0d7e 100644 --- a/tests/lib/AppFramework/Controller/PublicShareControllerTest.php +++ b/tests/lib/AppFramework/Controller/PublicShareControllerTest.php @@ -1,24 +1,8 @@ <?php + /** - * @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\AppFramework\Controller; @@ -28,18 +12,14 @@ use OCP\IRequest; use OCP\ISession; class TestController extends PublicShareController { - - /** @var string */ - private $hash; - - /** @var bool */ - private $isProtected; - - public function __construct(string $appName, IRequest $request, ISession $session, string $hash, bool $isProtected) { + public function __construct( + string $appName, + IRequest $request, + ISession $session, + private string $hash, + private bool $isProtected, + ) { parent::__construct($appName, $request, $session); - - $this->hash = $hash; - $this->isProtected = $isProtected; } protected function getPasswordHash(): string { @@ -56,7 +36,6 @@ class TestController extends PublicShareController { } class PublicShareControllerTest extends \Test\TestCase { - /** @var IRequest|\PHPUnit\Framework\MockObject\MockObject */ private $request; /** @var ISession|\PHPUnit\Framework\MockObject\MockObject */ @@ -69,14 +48,14 @@ class PublicShareControllerTest extends \Test\TestCase { $this->session = $this->createMock(ISession::class); } - public function testGetToken() { + public function testGetToken(): void { $controller = new TestController('app', $this->request, $this->session, 'hash', false); $controller->setToken('test'); $this->assertEquals('test', $controller->getToken()); } - public function dataIsAuthenticated() { + public static function dataIsAuthenticated(): array { return [ [false, 'token1', 'token1', 'hash1', 'hash1', true], [false, 'token1', 'token1', 'hash1', 'hash2', true], @@ -89,10 +68,8 @@ class PublicShareControllerTest extends \Test\TestCase { ]; } - /** - * @dataProvider dataIsAuthenticated - */ - public function testIsAuthenticatedNotPasswordProtected(bool $protected, string $token1, string $token2, string $hash1, string $hash2, bool $expected) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataIsAuthenticated')] + public function testIsAuthenticatedNotPasswordProtected(bool $protected, string $token1, string $token2, string $hash1, string $hash2, bool $expected): void { $controller = new TestController('app', $this->request, $this->session, $hash2, $protected); $this->session->method('get') diff --git a/tests/lib/AppFramework/Db/EntityTest.php b/tests/lib/AppFramework/Db/EntityTest.php index 17234849a2d..eab081e6ac6 100644 --- a/tests/lib/AppFramework/Db/EntityTest.php +++ b/tests/lib/AppFramework/Db/EntityTest.php @@ -1,29 +1,15 @@ <?php /** - * ownCloud - App Framework - * - * @author Bernhard Posselt - * @copyright 2012 Bernhard Posselt dev@bernhard-posselt.com - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\AppFramework\Db; use OCP\AppFramework\Db\Entity; +use OCP\DB\Types; use PHPUnit\Framework\Constraint\IsType; /** @@ -42,21 +28,46 @@ use PHPUnit\Framework\Constraint\IsType; * @method void setTrueOrFalse(bool $trueOrFalse) * @method bool getAnotherBool() * @method bool isAnotherBool() - * @method void setAnotherBool(bool $anotherBool) + * @method string getLongText() + * @method void setLongText(string $longText) + * @method \DateTime getTime() + * @method void setTime(\DateTime $time) + * @method \DateTimeImmutable getDatetime() + * @method void setDatetime(\DateTimeImmutable $datetime) */ class TestEntity extends Entity { - protected $name; protected $email; protected $testId; + protected $smallInt; + protected $bigInt; protected $preName; protected $trueOrFalse; protected $anotherBool; - - public function __construct($name = null) { - $this->addType('testId', 'integer'); + protected $text; + protected $longText; + protected $time; + protected $datetime; + + public function __construct( + protected $name = null, + ) { + $this->addType('testId', Types::INTEGER); + $this->addType('smallInt', Types::SMALLINT); + $this->addType('bigInt', Types::BIGINT); + $this->addType('anotherBool', Types::BOOLEAN); + $this->addType('text', Types::TEXT); + $this->addType('longText', Types::BLOB); + $this->addType('time', Types::TIME); + $this->addType('datetime', Types::DATETIME_IMMUTABLE); + + // Legacy types $this->addType('trueOrFalse', 'bool'); - $this->addType('anotherBool', 'boolean'); - $this->name = $name; + $this->addType('legacyInt', 'int'); + $this->addType('doubleNowFloat', 'double'); + } + + public function setAnotherBool(bool $anotherBool): void { + parent::setAnotherBool($anotherBool); } } @@ -70,7 +81,7 @@ class EntityTest extends \Test\TestCase { } - public function testResetUpdatedFields() { + public function testResetUpdatedFields(): void { $entity = new TestEntity(); $entity->setId(3); $entity->resetUpdatedFields(); @@ -79,19 +90,21 @@ class EntityTest extends \Test\TestCase { } - public function testFromRow() { + public function testFromRow(): void { $row = [ 'pre_name' => 'john', - 'email' => 'john@something.com' + 'email' => 'john@something.com', + 'another_bool' => 1, ]; $this->entity = TestEntity::fromRow($row); $this->assertEquals($row['pre_name'], $this->entity->getPreName()); $this->assertEquals($row['email'], $this->entity->getEmail()); + $this->assertEquals($row['another_bool'], $this->entity->getAnotherBool()); } - public function testGetSetId() { + public function testGetSetId(): void { $id = 3; $this->entity->setId(3); @@ -99,28 +112,28 @@ class EntityTest extends \Test\TestCase { } - public function testColumnToPropertyNoReplacement() { + public function testColumnToPropertyNoReplacement(): void { $column = 'my'; $this->assertEquals('my', $this->entity->columnToProperty($column)); } - public function testColumnToProperty() { + public function testColumnToProperty(): void { $column = 'my_attribute'; $this->assertEquals('myAttribute', $this->entity->columnToProperty($column)); } - public function testPropertyToColumnNoReplacement() { + public function testPropertyToColumnNoReplacement(): void { $property = 'my'; $this->assertEquals('my', $this->entity->propertyToColumn($property)); } - public function testSetterMarksFieldUpdated() { + public function testSetterMarksFieldUpdated(): void { $this->entity->setId(3); $this->assertContains('id', array_keys($this->entity->getUpdatedFields())); @@ -128,7 +141,7 @@ class EntityTest extends \Test\TestCase { - public function testCallShouldOnlyWorkForGetterSetter() { + public function testCallShouldOnlyWorkForGetterSetter(): void { $this->expectException(\BadFunctionCallException::class); $this->entity->something(); @@ -136,21 +149,21 @@ class EntityTest extends \Test\TestCase { - public function testGetterShouldFailIfAttributeNotDefined() { + public function testGetterShouldFailIfAttributeNotDefined(): void { $this->expectException(\BadFunctionCallException::class); $this->entity->getTest(); } - public function testSetterShouldFailIfAttributeNotDefined() { + public function testSetterShouldFailIfAttributeNotDefined(): void { $this->expectException(\BadFunctionCallException::class); $this->entity->setTest(); } - public function testFromRowShouldNotAssignEmptyArray() { + public function testFromRowShouldNotAssignEmptyArray(): void { $row = []; $entity2 = new TestEntity(); @@ -159,7 +172,7 @@ class EntityTest extends \Test\TestCase { } - public function testIdGetsConvertedToInt() { + public function testIdGetsConvertedToInt(): void { $row = ['id' => '4']; $this->entity = TestEntity::fromRow($row); @@ -167,7 +180,7 @@ class EntityTest extends \Test\TestCase { } - public function testSetType() { + public function testSetType(): void { $row = ['testId' => '4']; $this->entity = TestEntity::fromRow($row); @@ -175,7 +188,7 @@ class EntityTest extends \Test\TestCase { } - public function testFromParams() { + public function testFromParams(): void { $params = [ 'testId' => 4, 'email' => 'john@doe' @@ -188,7 +201,7 @@ class EntityTest extends \Test\TestCase { $this->assertTrue($entity instanceof TestEntity); } - public function testSlugify() { + public function testSlugify(): void { $entity = new TestEntity(); $entity->setName('Slugify this!'); $this->assertEquals('slugify-this', $entity->slugify('name')); @@ -197,45 +210,98 @@ class EntityTest extends \Test\TestCase { } - public function testSetterCasts() { + public static function dataSetterCasts(): array { + return [ + ['Id', '3', 3], + ['smallInt', '3', 3], + ['bigInt', '' . PHP_INT_MAX, PHP_INT_MAX], + ['trueOrFalse', 0, false], + ['trueOrFalse', 1, true], + ['anotherBool', 0, false], + ['anotherBool', 1, true], + ['text', 33, '33'], + ['longText', PHP_INT_MAX, '' . PHP_INT_MAX], + ]; + } + + + #[\PHPUnit\Framework\Attributes\DataProvider('dataSetterCasts')] + public function testSetterCasts(string $field, mixed $in, mixed $out): void { $entity = new TestEntity(); - $entity->setId('3'); - $this->assertSame(3, $entity->getId()); + $entity->{'set' . $field}($in); + $this->assertSame($out, $entity->{'get' . $field}()); } - public function testSetterDoesNotCastOnNull() { + public function testSetterDoesNotCastOnNull(): void { $entity = new TestEntity(); $entity->setId(null); $this->assertSame(null, $entity->getId()); } + public function testSetterConvertsResourcesToStringProperly(): void { + $string = 'Definitely a string'; + $stream = fopen('php://memory', 'r+'); + fwrite($stream, $string); + rewind($stream); + + $entity = new TestEntity(); + $entity->setLongText($stream); + fclose($stream); + $this->assertSame($string, $entity->getLongText()); + } + + public function testSetterConvertsDatetime() { + $entity = new TestEntity(); + $entity->setDatetime('2024-08-19 15:26:00'); + $this->assertEquals(new \DateTimeImmutable('2024-08-19 15:26:00'), $entity->getDatetime()); + } + + public function testSetterDoesNotConvertNullOnDatetime() { + $entity = new TestEntity(); + $entity->setDatetime(null); + $this->assertNull($entity->getDatetime()); + } + + public function testSetterConvertsTime() { + $entity = new TestEntity(); + $entity->setTime('15:26:00'); + $this->assertEquals(new \DateTime('15:26:00'), $entity->getTime()); + } - public function testGetFieldTypes() { + public function testGetFieldTypes(): void { $entity = new TestEntity(); $this->assertEquals([ - 'id' => 'integer', - 'testId' => 'integer', - 'trueOrFalse' => 'bool', - 'anotherBool' => 'boolean', + 'id' => Types::INTEGER, + 'testId' => Types::INTEGER, + 'smallInt' => Types::SMALLINT, + 'bigInt' => Types::BIGINT, + 'anotherBool' => Types::BOOLEAN, + 'text' => Types::TEXT, + 'longText' => Types::BLOB, + 'time' => Types::TIME, + 'datetime' => Types::DATETIME_IMMUTABLE, + 'trueOrFalse' => Types::BOOLEAN, + 'legacyInt' => Types::INTEGER, + 'doubleNowFloat' => Types::FLOAT, ], $entity->getFieldTypes()); } - public function testGetItInt() { + public function testGetItInt(): void { $entity = new TestEntity(); $entity->setId(3); - $this->assertEquals('integer', gettype($entity->getId())); + $this->assertEquals(Types::INTEGER, gettype($entity->getId())); } - public function testFieldsNotMarkedUpdatedIfNothingChanges() { + public function testFieldsNotMarkedUpdatedIfNothingChanges(): void { $entity = new TestEntity('hey'); $entity->setName('hey'); $this->assertEquals(0, count($entity->getUpdatedFields())); } - public function testIsGetter() { + public function testIsGetter(): void { $entity = new TestEntity(); $entity->setTrueOrFalse(false); $entity->setAnotherBool(false); @@ -244,7 +310,7 @@ class EntityTest extends \Test\TestCase { } - public function testIsGetterShoudFailForOtherType() { + public function testIsGetterShoudFailForOtherType(): void { $this->expectException(\BadFunctionCallException::class); $entity = new TestEntity(); diff --git a/tests/lib/AppFramework/Db/MapperTest.php b/tests/lib/AppFramework/Db/MapperTest.php deleted file mode 100644 index e5a4b63b7a3..00000000000 --- a/tests/lib/AppFramework/Db/MapperTest.php +++ /dev/null @@ -1,300 +0,0 @@ -<?php - -/** - * ownCloud - App Framework - * - * @author Bernhard Posselt - * @copyright 2012 Bernhard Posselt dev@bernhard-posselt.com - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * - */ - -namespace Test\AppFramework\Db; - -use OCP\AppFramework\Db\DoesNotExistException; -use OCP\AppFramework\Db\Entity; -use OCP\AppFramework\Db\Mapper; -use OCP\AppFramework\Db\MultipleObjectsReturnedException; -use OCP\IDBConnection; - -/** - * @method integer getId() - * @method void setId(integer $id) - * @method string getEmail() - * @method void setEmail(string $email) - * @method string getPreName() - * @method void setPreName(string $preName) - */ -class Example extends Entity { - protected $preName; - protected $email; -}; - - -class ExampleMapper extends Mapper { - public function __construct(IDBConnection $db) { - parent::__construct($db, 'table'); - } - public function find($table, $id) { - return $this->findOneQuery($table, $id); - } - public function findOneEntity($table, $id) { - return $this->findEntity($table, $id); - } - public function findAllEntities($table) { - return $this->findEntities($table); - } - public function mapRow($row) { - return $this->mapRowToEntity($row); - } - public function execSql($sql, $params) { - return $this->execute($sql, $params); - } -} - - -class MapperTest extends MapperTestUtility { - - /** - * @var Mapper - */ - private $mapper; - - protected function setUp(): void { - parent::setUp(); - $this->mapper = new ExampleMapper($this->db); - } - - - public function testMapperShouldSetTableName() { - $this->assertEquals('*PREFIX*table', $this->mapper->getTableName()); - } - - - public function testFindQuery() { - $sql = 'hi'; - $params = ['jo']; - $rows = [ - ['hi'] - ]; - $this->setMapperResult($sql, $params, $rows); - $this->mapper->find($sql, $params); - } - - public function testFindEntity() { - $sql = 'hi'; - $params = ['jo']; - $rows = [ - ['pre_name' => 'hi'] - ]; - $this->setMapperResult($sql, $params, $rows, null, null, true); - $this->mapper->findOneEntity($sql, $params); - } - - public function testFindNotFound() { - $sql = 'hi'; - $params = ['jo']; - $rows = []; - $this->setMapperResult($sql, $params, $rows); - $this->expectException(DoesNotExistException::class); - $this->mapper->find($sql, $params); - } - - public function testFindEntityNotFound() { - $sql = 'hi'; - $params = ['jo']; - $rows = []; - $this->setMapperResult($sql, $params, $rows, null, null, true); - $this->expectException(DoesNotExistException::class); - $this->mapper->findOneEntity($sql, $params); - } - - public function testFindMultiple() { - $sql = 'hi'; - $params = ['jo']; - $rows = [ - ['jo'], ['ho'] - ]; - $this->setMapperResult($sql, $params, $rows, null, null, true); - $this->expectException(MultipleObjectsReturnedException::class); - $this->mapper->find($sql, $params); - } - - public function testFindEntityMultiple() { - $sql = 'hi'; - $params = ['jo']; - $rows = [ - ['jo'], ['ho'] - ]; - $this->setMapperResult($sql, $params, $rows, null, null, true); - $this->expectException(MultipleObjectsReturnedException::class); - $this->mapper->findOneEntity($sql, $params); - } - - - public function testDelete() { - $sql = 'DELETE FROM `*PREFIX*table` WHERE `id` = ?'; - $params = [2]; - - $this->setMapperResult($sql, $params, [], null, null, true); - $entity = new Example(); - $entity->setId($params[0]); - - $this->mapper->delete($entity); - } - - - public function testCreate() { - $this->db->expects($this->once()) - ->method('lastInsertId') - ->with($this->equalTo('*PREFIX*table')) - ->willReturn(3); - $this->mapper = new ExampleMapper($this->db); - - $sql = 'INSERT INTO `*PREFIX*table`(`pre_name`,`email`) ' . - 'VALUES(?,?)'; - $params = ['john', 'my@email']; - $entity = new Example(); - $entity->setPreName($params[0]); - $entity->setEmail($params[1]); - - $this->setMapperResult($sql, $params, [], null, null, true); - - $this->mapper->insert($entity); - } - - - public function testCreateShouldReturnItemWithCorrectInsertId() { - $this->db->expects($this->once()) - ->method('lastInsertId') - ->with($this->equalTo('*PREFIX*table')) - ->willReturn(3); - $this->mapper = new ExampleMapper($this->db); - - $sql = 'INSERT INTO `*PREFIX*table`(`pre_name`,`email`) ' . - 'VALUES(?,?)'; - $params = ['john', 'my@email']; - $entity = new Example(); - $entity->setPreName($params[0]); - $entity->setEmail($params[1]); - - $this->setMapperResult($sql, $params); - - $result = $this->mapper->insert($entity); - - $this->assertEquals(3, $result->getId()); - } - - - public function testAssocParameters() { - $sql = 'test'; - $params = [':test' => 1, ':a' => 2]; - - $this->setMapperResult($sql, $params); - $this->mapper->execSql($sql, $params); - } - - - public function testUpdate() { - $sql = 'UPDATE `*PREFIX*table` ' . - 'SET ' . - '`pre_name` = ?,'. - '`email` = ? ' . - 'WHERE `id` = ?'; - - $params = ['john', 'my@email', 1]; - $entity = new Example(); - $entity->setPreName($params[0]); - $entity->setEmail($params[1]); - $entity->setId($params[2]); - - $this->setMapperResult($sql, $params, [], null, null, true); - - $this->mapper->update($entity); - } - - - public function testUpdateNoId() { - $params = ['john', 'my@email']; - $entity = new Example(); - $entity->setPreName($params[0]); - $entity->setEmail($params[1]); - - $this->expectException(\InvalidArgumentException::class); - - $this->mapper->update($entity); - } - - - public function testUpdateNothingChangedNoQuery() { - $params = ['john', 'my@email']; - $entity = new Example(); - $entity->setId(3); - $entity->setEmail($params[1]); - $entity->resetUpdatedFields(); - - $this->db->expects($this->never()) - ->method('prepare'); - - $this->mapper->update($entity); - } - - - public function testMapRowToEntity() { - $entity1 = $this->mapper->mapRow(['pre_name' => 'test1', 'email' => 'test2']); - $entity2 = new Example(); - $entity2->setPreName('test1'); - $entity2->setEmail('test2'); - $entity2->resetUpdatedFields(); - $this->assertEquals($entity2, $entity1); - } - - public function testFindEntities() { - $sql = 'hi'; - $rows = [ - ['pre_name' => 'hi'] - ]; - $entity = new Example(); - $entity->setPreName('hi'); - $entity->resetUpdatedFields(); - $this->setMapperResult($sql, [], $rows, null, null, true); - $result = $this->mapper->findAllEntities($sql); - $this->assertEquals([$entity], $result); - } - - public function testFindEntitiesNotFound() { - $sql = 'hi'; - $rows = []; - $this->setMapperResult($sql, [], $rows); - $result = $this->mapper->findAllEntities($sql); - $this->assertEquals([], $result); - } - - public function testFindEntitiesMultiple() { - $sql = 'hi'; - $rows = [ - ['pre_name' => 'jo'], ['email' => 'ho'] - ]; - $entity1 = new Example(); - $entity1->setPreName('jo'); - $entity1->resetUpdatedFields(); - $entity2 = new Example(); - $entity2->setEmail('ho'); - $entity2->resetUpdatedFields(); - $this->setMapperResult($sql, [], $rows); - $result = $this->mapper->findAllEntities($sql); - $this->assertEquals([$entity1, $entity2], $result); - } -} diff --git a/tests/lib/AppFramework/Db/MapperTestUtility.php b/tests/lib/AppFramework/Db/MapperTestUtility.php deleted file mode 100644 index e17b875e4c4..00000000000 --- a/tests/lib/AppFramework/Db/MapperTestUtility.php +++ /dev/null @@ -1,206 +0,0 @@ -<?php - -/** - * ownCloud - App Framework - * - * @author Bernhard Posselt - * @copyright 2012 Bernhard Posselt dev@bernhard-posselt.com - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * - */ - -namespace Test\AppFramework\Db; - -use OCP\DB\IPreparedStatement; -use OCP\DB\IResult; - -/** - * Simple utility class for testing mappers - */ -abstract class MapperTestUtility extends \Test\TestCase { - protected $db; - private $statement; - private $queryAt; - private $prepareAt; - private $fetchAt; - private $iterators; - - - /** - * Run this function before the actual test to either set or initialize the - * db. After this the db can be accessed by using $this->db - */ - protected function setUp(): void { - parent::setUp(); - - $this->db = $this->getMockBuilder( - '\OCP\IDBConnection') - ->disableOriginalConstructor() - ->getMock(); - - $this->statement = $this->createMock(IPreparedStatement::class); - $this->queryAt = 0; - $this->prepareAt = 0; - $this->iterators = []; - $this->fetchAt = 0; - } - - /** - * Checks if an array is associative - * @param array $array - * @return bool true if associative - */ - private function isAssocArray(array $array) { - return array_values($array) !== $array; - } - - /** - * Returns the correct PDO constant based on the value type - * @param $value - * @return int PDO constant - */ - private function getPDOType($value) { - switch (gettype($value)) { - case 'integer': - return \PDO::PARAM_INT; - case 'boolean': - return \PDO::PARAM_BOOL; - default: - return \PDO::PARAM_STR; - } - } - - /** - * Create mocks and set expected results for database queries - * @param string $sql the sql query that you expect to receive - * @param array $arguments the expected arguments for the prepare query - * method - * @param array $returnRows the rows that should be returned for the result - * of the database query. If not provided, it wont be assumed that fetch - * will be called on the result - */ - protected function setMapperResult($sql, $arguments = [], $returnRows = [], - $limit = null, $offset = null, $expectClose = false) { - if ($limit === null && $offset === null) { - $this->db->expects($this->at($this->prepareAt)) - ->method('prepare') - ->with($this->equalTo($sql)) - ->will(($this->returnValue($this->statement))); - } elseif ($limit !== null && $offset === null) { - $this->db->expects($this->at($this->prepareAt)) - ->method('prepare') - ->with($this->equalTo($sql), $this->equalTo($limit)) - ->will(($this->returnValue($this->statement))); - } elseif ($limit === null && $offset !== null) { - $this->db->expects($this->at($this->prepareAt)) - ->method('prepare') - ->with($this->equalTo($sql), - $this->equalTo(null), - $this->equalTo($offset)) - ->will(($this->returnValue($this->statement))); - } else { - $this->db->expects($this->at($this->prepareAt)) - ->method('prepare') - ->with($this->equalTo($sql), - $this->equalTo($limit), - $this->equalTo($offset)) - ->will(($this->returnValue($this->statement))); - } - - $this->iterators[] = new ArgumentIterator($returnRows); - - $iterators = $this->iterators; - $fetchAt = $this->fetchAt; - - $this->statement->expects($this->any()) - ->method('fetch') - ->willReturnCallback( - function () use ($iterators, $fetchAt) { - $iterator = $iterators[$fetchAt]; - $result = $iterator->next(); - - if ($result === false) { - $fetchAt++; - } - - $this->queryAt++; - - return $result; - } - ); - - if ($this->isAssocArray($arguments)) { - foreach ($arguments as $key => $argument) { - $pdoConstant = $this->getPDOType($argument); - $this->statement->expects($this->at($this->queryAt)) - ->method('bindValue') - ->with($this->equalTo($key), - $this->equalTo($argument), - $this->equalTo($pdoConstant)); - $this->queryAt++; - } - } else { - $index = 1; - foreach ($arguments as $argument) { - $pdoConstant = $this->getPDOType($argument); - $this->statement->expects($this->at($this->queryAt)) - ->method('bindValue') - ->with($this->equalTo($index), - $this->equalTo($argument), - $this->equalTo($pdoConstant)); - $index++; - $this->queryAt++; - } - } - - $this->statement->expects($this->at($this->queryAt)) - ->method('execute') - ->willReturnCallback(function ($sql, $p = null, $o = null, $s = null) { - return $this->createMock(IResult::class); - }); - $this->queryAt++; - - - - if ($expectClose) { - $closing = $this->at($this->queryAt); - } else { - $closing = $this->any(); - } - $this->statement->expects($closing)->method('closeCursor'); - $this->queryAt++; - - $this->prepareAt++; - $this->fetchAt++; - } -} - - -class ArgumentIterator { - private $arguments; - - public function __construct($arguments) { - $this->arguments = $arguments; - } - - public function next() { - $result = array_shift($this->arguments); - if ($result === null) { - return false; - } else { - return $result; - } - } -} diff --git a/tests/lib/AppFramework/Db/QBMapperDBTest.php b/tests/lib/AppFramework/Db/QBMapperDBTest.php new file mode 100644 index 00000000000..614f1099644 --- /dev/null +++ b/tests/lib/AppFramework/Db/QBMapperDBTest.php @@ -0,0 +1,160 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace Test\AppFramework\Db; + +use Doctrine\DBAL\Schema\SchemaException; +use OCP\AppFramework\Db\Entity; +use OCP\AppFramework\Db\QBMapper; +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\DB\Types; +use OCP\IConfig; +use OCP\IDBConnection; +use OCP\Server; +use Test\TestCase; + +/** + * @method void setTime(?\DateTime $time) + * @method ?\DateTime getTime() + * @method void setDatetime(?\DateTimeImmutable $datetime) + * @method ?\DateTimeImmutable getDatetime() + */ +class QBDBTestEntity extends Entity { + protected ?\DateTime $time = null; + protected ?\DateTimeImmutable $datetime = null; + + public function __construct() { + $this->addType('time', Types::TIME); + $this->addType('datetime', Types::DATETIME_IMMUTABLE); + } +} + +/** + * Class QBDBTestMapper + * + * @package Test\AppFramework\Db + */ +class QBDBTestMapper extends QBMapper { + public function __construct(IDBConnection $db) { + parent::__construct($db, 'testing', QBDBTestEntity::class); + } + + public function getParameterTypeForPropertyForTest(Entity $entity, string $property) { + return parent::getParameterTypeForProperty($entity, $property); + } + + public function getById(int $id): QBDBTestEntity { + $qb = $this->db->getQueryBuilder(); + $query = $qb + ->select('*') + ->from($this->tableName) + ->where( + $qb->expr()->eq('id', $qb->createPositionalParameter($id, IQueryBuilder::PARAM_INT)), + ); + return $this->findEntity($query); + } +} + +/** + * Test real database handling (serialization) + * @group DB + */ +class QBMapperDBTest extends TestCase { + /** @var \Doctrine\DBAL\Connection|IDBConnection */ + protected $connection; + protected $schemaSetup = false; + + protected function setUp(): void { + parent::setUp(); + + $this->connection = Server::get(IDBConnection::class); + $this->prepareTestingTable(); + } + + public function testInsertDateTime(): void { + $mapper = new QBDBTestMapper($this->connection); + $entity = new QBDBTestEntity(); + $entity->setTime(new \DateTime('2003-01-01 12:34:00')); + $entity->setDatetime(new \DateTimeImmutable('2000-01-01 23:45:00')); + + $result = $mapper->insert($entity); + $this->assertNotNull($result->getId()); + } + + public function testRetrieveDateTime(): void { + $time = new \DateTime('2000-01-01 01:01:00'); + $datetime = new \DateTimeImmutable('2000-01-01 02:02:00'); + + $mapper = new QBDBTestMapper($this->connection); + $entity = new QBDBTestEntity(); + $entity->setTime($time); + $entity->setDatetime($datetime); + + $result = $mapper->insert($entity); + $this->assertNotNull($result->getId()); + + $dbEntity = $mapper->getById($result->getId()); + $this->assertEquals($time->format('H:i:s'), $dbEntity->getTime()->format('H:i:s')); + $this->assertEquals($datetime->format('Y-m-d H:i:s'), $dbEntity->getDatetime()->format('Y-m-d H:i:s')); + // The date is not saved for "time" + $this->assertNotEquals($time->format('Y'), $dbEntity->getTime()->format('Y')); + } + + public function testUpdateDateTime(): void { + $time = new \DateTime('2000-01-01 01:01:00'); + $datetime = new \DateTimeImmutable('2000-01-01 02:02:00'); + + $mapper = new QBDBTestMapper($this->connection); + $entity = new QBDBTestEntity(); + $entity->setTime('now'); + $entity->setDatetime('now'); + + /** @var QBDBTestEntity */ + $entity = $mapper->insert($entity); + $this->assertNotNull($entity->getId()); + + // Update the values + $entity->setTime($time); + $entity->setDatetime($datetime); + $mapper->update($entity); + + $dbEntity = $mapper->getById($entity->getId()); + $this->assertEquals($time->format('H:i:s'), $dbEntity->getTime()->format('H:i:s')); + $this->assertEquals($datetime->format('Y-m-d H:i:s'), $dbEntity->getDatetime()->format('Y-m-d H:i:s')); + } + + protected function prepareTestingTable(): void { + if ($this->schemaSetup) { + $this->connection->getQueryBuilder()->delete('testing')->executeStatement(); + } + + $prefix = Server::get(IConfig::class)->getSystemValueString('dbtableprefix', 'oc_'); + $schema = $this->connection->createSchema(); + try { + $schema->getTable($prefix . 'testing'); + $this->connection->getQueryBuilder()->delete('testing')->executeStatement(); + } catch (SchemaException $e) { + $this->schemaSetup = true; + $table = $schema->createTable($prefix . 'testing'); + $table->addColumn('id', Types::BIGINT, [ + 'autoincrement' => true, + 'notnull' => true, + ]); + + $table->addColumn('time', Types::TIME, [ + 'notnull' => false, + ]); + + $table->addColumn('datetime', Types::DATETIME_IMMUTABLE, [ + 'notnull' => false, + ]); + + $table->setPrimaryKey(['id']); + $this->connection->migrateToSchema($schema); + } + } +} diff --git a/tests/lib/AppFramework/Db/QBMapperTest.php b/tests/lib/AppFramework/Db/QBMapperTest.php index 96d319923b3..0f18ef3f204 100644 --- a/tests/lib/AppFramework/Db/QBMapperTest.php +++ b/tests/lib/AppFramework/Db/QBMapperTest.php @@ -1,24 +1,8 @@ <?php + /** - * @copyright 2019, Marius David Wieschollek <git.public@mdns.eu> - * - * @author Marius David Wieschollek <git.public@mdns.eu> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\AppFramework\Db; @@ -27,7 +11,9 @@ use OCP\AppFramework\Db\Entity; use OCP\AppFramework\Db\QBMapper; use OCP\DB\QueryBuilder\IExpressionBuilder; use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\DB\Types; use OCP\IDBConnection; +use PHPUnit\Framework\MockObject\MockObject; /** * @method bool getBoolProp() @@ -40,6 +26,8 @@ use OCP\IDBConnection; * @method void setBooleanProp(bool $booleanProp) * @method integer getIntegerProp() * @method void setIntegerProp(integer $integerProp) + * @method ?\DateTimeImmutable getDatetimeProp() + * @method void setDatetimeProp(?\DateTimeImmutable $datetime) */ class QBTestEntity extends Entity { protected $intProp; @@ -48,14 +36,16 @@ class QBTestEntity extends Entity { protected $integerProp; protected $booleanProp; protected $jsonProp; + protected $datetimeProp; public function __construct() { $this->addType('intProp', 'int'); $this->addType('boolProp', 'bool'); - $this->addType('stringProp', 'string'); - $this->addType('integerProp', 'integer'); - $this->addType('booleanProp', 'boolean'); - $this->addType('jsonProp', 'json'); + $this->addType('stringProp', Types::STRING); + $this->addType('integerProp', Types::INTEGER); + $this->addType('booleanProp', Types::BOOLEAN); + $this->addType('jsonProp', Types::JSON); + $this->addType('datetimeProp', Types::DATETIME_IMMUTABLE); } } @@ -81,25 +71,10 @@ class QBTestMapper extends QBMapper { */ class QBMapperTest extends \Test\TestCase { - /** - * @var \PHPUnit\Framework\MockObject\MockObject|IDBConnection - */ - protected $db; - - /** - * @var \PHPUnit\Framework\MockObject\MockObject|IQueryBuilder - */ - protected $qb; - - /** - * @var \PHPUnit\Framework\MockObject\MockObject|IExpressionBuilder - */ - protected $expr; - - /** - * @var \Test\AppFramework\Db\QBTestMapper - */ - protected $mapper; + protected IDBConnection&MockObject $db; + protected IQueryBuilder&MockObject $qb; + protected IExpressionBuilder&MockObject $expr; + protected QBTestMapper $mapper; /** * @throws \ReflectionException @@ -125,45 +100,60 @@ class QBMapperTest extends \Test\TestCase { $this->mapper = new QBTestMapper($this->db); } - - public function testInsertEntityParameterTypeMapping() { + + public function testInsertEntityParameterTypeMapping(): void { + $datetime = new \DateTimeImmutable(); $entity = new QBTestEntity(); $entity->setIntProp(123); $entity->setBoolProp(true); $entity->setStringProp('string'); $entity->setIntegerProp(456); $entity->setBooleanProp(false); + $entity->setDatetimeProp($datetime); $intParam = $this->qb->createNamedParameter('int_prop', IQueryBuilder::PARAM_INT); $boolParam = $this->qb->createNamedParameter('bool_prop', IQueryBuilder::PARAM_BOOL); $stringParam = $this->qb->createNamedParameter('string_prop', IQueryBuilder::PARAM_STR); $integerParam = $this->qb->createNamedParameter('integer_prop', IQueryBuilder::PARAM_INT); $booleanParam = $this->qb->createNamedParameter('boolean_prop', IQueryBuilder::PARAM_BOOL); - - $this->qb->expects($this->exactly(5)) + $datetimeParam = $this->qb->createNamedParameter('datetime_prop', IQueryBuilder::PARAM_DATETIME_IMMUTABLE); + + $createNamedParameterCalls = [ + [123, IQueryBuilder::PARAM_INT, null], + [true, IQueryBuilder::PARAM_BOOL, null], + ['string', IQueryBuilder::PARAM_STR, null], + [456, IQueryBuilder::PARAM_INT, null], + [false, IQueryBuilder::PARAM_BOOL, null], + [$datetime, IQueryBuilder::PARAM_DATETIME_IMMUTABLE, null], + ]; + $this->qb->expects($this->exactly(6)) ->method('createNamedParameter') - ->withConsecutive( - [$this->equalTo(123), $this->equalTo(IQueryBuilder::PARAM_INT)], - [$this->equalTo(true), $this->equalTo(IQueryBuilder::PARAM_BOOL)], - [$this->equalTo('string'), $this->equalTo(IQueryBuilder::PARAM_STR)], - [$this->equalTo(456), $this->equalTo(IQueryBuilder::PARAM_INT)], - [$this->equalTo(false), $this->equalTo(IQueryBuilder::PARAM_BOOL)] - ); - $this->qb->expects($this->exactly(5)) + ->willReturnCallback(function () use (&$createNamedParameterCalls): void { + $expected = array_shift($createNamedParameterCalls); + $this->assertEquals($expected, func_get_args()); + }); + + $setValueCalls = [ + ['int_prop', $intParam], + ['bool_prop', $boolParam], + ['string_prop', $stringParam], + ['integer_prop', $integerParam], + ['boolean_prop', $booleanParam], + ['datetime_prop', $datetimeParam], + ]; + $this->qb->expects($this->exactly(6)) ->method('setValue') - ->withConsecutive( - [$this->equalTo('int_prop'), $this->equalTo($intParam)], - [$this->equalTo('bool_prop'), $this->equalTo($boolParam)], - [$this->equalTo('string_prop'), $this->equalTo($stringParam)], - [$this->equalTo('integer_prop'), $this->equalTo($integerParam)], - [$this->equalTo('boolean_prop'), $this->equalTo($booleanParam)] - ); + ->willReturnCallback(function () use (&$setValueCalls): void { + $expected = array_shift($setValueCalls); + $this->assertEquals($expected, func_get_args()); + }); $this->mapper->insert($entity); } - - public function testUpdateEntityParameterTypeMapping() { + + public function testUpdateEntityParameterTypeMapping(): void { + $datetime = new \DateTimeImmutable(); $entity = new QBTestEntity(); $entity->setId(789); $entity->setIntProp(123); @@ -171,7 +161,8 @@ class QBMapperTest extends \Test\TestCase { $entity->setStringProp('string'); $entity->setIntegerProp(456); $entity->setBooleanProp(false); - $entity->setJsonProp(["hello" => "world"]); + $entity->setJsonProp(['hello' => 'world']); + $entity->setDatetimeProp($datetime); $idParam = $this->qb->createNamedParameter('id', IQueryBuilder::PARAM_INT); $intParam = $this->qb->createNamedParameter('int_prop', IQueryBuilder::PARAM_INT); @@ -180,29 +171,40 @@ class QBMapperTest extends \Test\TestCase { $integerParam = $this->qb->createNamedParameter('integer_prop', IQueryBuilder::PARAM_INT); $booleanParam = $this->qb->createNamedParameter('boolean_prop', IQueryBuilder::PARAM_BOOL); $jsonParam = $this->qb->createNamedParameter('json_prop', IQueryBuilder::PARAM_JSON); - - $this->qb->expects($this->exactly(7)) + $datetimeParam = $this->qb->createNamedParameter('datetime_prop', IQueryBuilder::PARAM_DATETIME_IMMUTABLE); + + $createNamedParameterCalls = [ + [123, IQueryBuilder::PARAM_INT, null], + [true, IQueryBuilder::PARAM_BOOL, null], + ['string', IQueryBuilder::PARAM_STR, null], + [456, IQueryBuilder::PARAM_INT, null], + [false, IQueryBuilder::PARAM_BOOL, null], + [['hello' => 'world'], IQueryBuilder::PARAM_JSON, null], + [$datetime, IQueryBuilder::PARAM_DATETIME_IMMUTABLE, null], + [789, IQueryBuilder::PARAM_INT, null], + ]; + $this->qb->expects($this->exactly(8)) ->method('createNamedParameter') - ->withConsecutive( - [$this->equalTo(123), $this->equalTo(IQueryBuilder::PARAM_INT)], - [$this->equalTo(true), $this->equalTo(IQueryBuilder::PARAM_BOOL)], - [$this->equalTo('string'), $this->equalTo(IQueryBuilder::PARAM_STR)], - [$this->equalTo(456), $this->equalTo(IQueryBuilder::PARAM_INT)], - [$this->equalTo(false), $this->equalTo(IQueryBuilder::PARAM_BOOL)], - [$this->equalTo(["hello" => "world"]), $this->equalTo(IQueryBuilder::PARAM_JSON)], - [$this->equalTo(789), $this->equalTo(IQueryBuilder::PARAM_INT)], - ); - - $this->qb->expects($this->exactly(6)) + ->willReturnCallback(function () use (&$createNamedParameterCalls): void { + $expected = array_shift($createNamedParameterCalls); + $this->assertEquals($expected, func_get_args()); + }); + + $setCalls = [ + ['int_prop', $intParam], + ['bool_prop', $boolParam], + ['string_prop', $stringParam], + ['integer_prop', $integerParam], + ['boolean_prop', $booleanParam], + ['json_prop', $datetimeParam], + ['datetime_prop', $datetimeParam], + ]; + $this->qb->expects($this->exactly(7)) ->method('set') - ->withConsecutive( - [$this->equalTo('int_prop'), $this->equalTo($intParam)], - [$this->equalTo('bool_prop'), $this->equalTo($boolParam)], - [$this->equalTo('string_prop'), $this->equalTo($stringParam)], - [$this->equalTo('integer_prop'), $this->equalTo($integerParam)], - [$this->equalTo('boolean_prop'), $this->equalTo($booleanParam)], - [$this->equalTo('json_prop'), $this->equalTo($jsonParam)] - ); + ->willReturnCallback(function () use (&$setCalls): void { + $expected = array_shift($setCalls); + $this->assertEquals($expected, func_get_args()); + }); $this->expr->expects($this->once()) ->method('eq') @@ -212,8 +214,8 @@ class QBMapperTest extends \Test\TestCase { $this->mapper->update($entity); } - - public function testGetParameterTypeForProperty() { + + public function testGetParameterTypeForProperty(): void { $entity = new QBTestEntity(); $intType = $this->mapper->getParameterTypeForPropertyForTest($entity, 'intProp'); @@ -234,6 +236,9 @@ class QBMapperTest extends \Test\TestCase { $jsonType = $this->mapper->getParameterTypeForPropertyForTest($entity, 'jsonProp'); $this->assertEquals(IQueryBuilder::PARAM_JSON, $jsonType, 'JSON type property mapping incorrect'); + $datetimeType = $this->mapper->getParameterTypeForPropertyForTest($entity, 'datetimeProp'); + $this->assertEquals(IQueryBuilder::PARAM_DATETIME_IMMUTABLE, $datetimeType, 'DateTimeImmutable type property mapping incorrect'); + $unknownType = $this->mapper->getParameterTypeForPropertyForTest($entity, 'someProp'); $this->assertEquals(IQueryBuilder::PARAM_STR, $unknownType, 'Unknown type property mapping incorrect'); } diff --git a/tests/lib/AppFramework/Db/TransactionalTest.php b/tests/lib/AppFramework/Db/TransactionalTest.php index 060badca0eb..72a3d9ae59f 100644 --- a/tests/lib/AppFramework/Db/TransactionalTest.php +++ b/tests/lib/AppFramework/Db/TransactionalTest.php @@ -1,25 +1,9 @@ <?php declare(strict_types=1); -/* - * @copyright 2022 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author 2022 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. +/** + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace lib\AppFramework\Db; @@ -31,7 +15,6 @@ use RuntimeException; use Test\TestCase; class TransactionalTest extends TestCase { - /** @var IDBConnection|MockObject */ private IDBConnection $db; @@ -45,14 +28,13 @@ class TransactionalTest extends TestCase { $test = new class($this->db) { use TTransactional; - private IDBConnection $db; - - public function __construct(IDBConnection $db) { - $this->db = $db; + public function __construct( + private IDBConnection $db, + ) { } public function fail(): void { - $this->atomic(function () { + $this->atomic(function (): void { throw new RuntimeException('nope'); }, $this->db); } @@ -72,10 +54,9 @@ class TransactionalTest extends TestCase { $test = new class($this->db) { use TTransactional; - private IDBConnection $db; - - public function __construct(IDBConnection $db) { - $this->db = $db; + public function __construct( + private IDBConnection $db, + ) { } public function succeed(): int { diff --git a/tests/lib/AppFramework/DependencyInjection/DIContainerTest.php b/tests/lib/AppFramework/DependencyInjection/DIContainerTest.php index 9a3d40d1c6b..31188b12f14 100644 --- a/tests/lib/AppFramework/DependencyInjection/DIContainerTest.php +++ b/tests/lib/AppFramework/DependencyInjection/DIContainerTest.php @@ -1,72 +1,60 @@ <?php /** - * ownCloud - App Framework - * - * @author Bernhard Posselt - * @author Morris Jobke - * @copyright 2012 Bernhard Posselt <dev@bernhard-posselt.com> - * @copyright 2013 Morris Jobke <morris.jobke@gmail.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\AppFramework\DependencyInjection; +use OC\AppFramework\Bootstrap\Coordinator; +use OC\AppFramework\Bootstrap\MiddlewareRegistration; +use OC\AppFramework\Bootstrap\RegistrationContext; use OC\AppFramework\DependencyInjection\DIContainer; use OC\AppFramework\Http\Request; use OC\AppFramework\Middleware\Security\SecurityMiddleware; +use OCP\AppFramework\Middleware; use OCP\AppFramework\QueryException; use OCP\IConfig; use OCP\IRequestId; +use PHPUnit\Framework\MockObject\MockObject; /** * @group DB */ class DIContainerTest extends \Test\TestCase { - - /** @var DIContainer|\PHPUnit\Framework\MockObject\MockObject */ - private $container; + private DIContainer&MockObject $container; protected function setUp(): void { parent::setUp(); $this->container = $this->getMockBuilder(DIContainer::class) - ->setMethods(['isAdminUser']) + ->onlyMethods(['isAdminUser']) ->setConstructorArgs(['name']) ->getMock(); } - public function testProvidesRequest() { + public function testProvidesRequest(): void { $this->assertTrue(isset($this->container['Request'])); } - public function testProvidesMiddlewareDispatcher() { + public function testProvidesMiddlewareDispatcher(): void { $this->assertTrue(isset($this->container['MiddlewareDispatcher'])); } - public function testProvidesAppName() { + public function testProvidesAppName(): void { $this->assertTrue(isset($this->container['AppName'])); + $this->assertTrue(isset($this->container['appName'])); } - public function testAppNameIsSetCorrectly() { + public function testAppNameIsSetCorrectly(): void { $this->assertEquals('name', $this->container['AppName']); + $this->assertEquals('name', $this->container['appName']); } - public function testMiddlewareDispatcherIncludesSecurityMiddleware() { + public function testMiddlewareDispatcherIncludesSecurityMiddleware(): void { $this->container['Request'] = new Request( ['method' => 'GET'], $this->createMock(IRequestId::class), @@ -85,7 +73,71 @@ class DIContainerTest extends \Test\TestCase { $this->assertTrue($found); } - public function testInvalidAppClass() { + public function testMiddlewareDispatcherIncludesBootstrapMiddlewares(): void { + $coordinator = $this->createMock(Coordinator::class); + $this->container[Coordinator::class] = $coordinator; + $this->container['Request'] = $this->createMock(Request::class); + $registrationContext = $this->createMock(RegistrationContext::class); + $registrationContext->method('getMiddlewareRegistrations') + ->willReturn([ + new MiddlewareRegistration($this->container['appName'], 'foo', false), + new MiddlewareRegistration('otherapp', 'bar', false), + ]); + $this->container['foo'] = new class extends Middleware { + }; + $this->container['bar'] = new class extends Middleware { + }; + $coordinator->method('getRegistrationContext')->willReturn($registrationContext); + + $dispatcher = $this->container['MiddlewareDispatcher']; + + $middlewares = $dispatcher->getMiddlewares(); + self::assertNotEmpty($middlewares); + foreach ($middlewares as $middleware) { + if ($middleware === $this->container['bar']) { + $this->fail('Container must not register this middleware'); + } + if ($middleware === $this->container['foo']) { + // It is done + return; + } + } + $this->fail('Bootstrap registered middleware not found'); + } + + public function testMiddlewareDispatcherIncludesGlobalBootstrapMiddlewares(): void { + $coordinator = $this->createMock(Coordinator::class); + $this->container[Coordinator::class] = $coordinator; + $this->container['Request'] = $this->createMock(Request::class); + $registrationContext = $this->createMock(RegistrationContext::class); + $registrationContext->method('getMiddlewareRegistrations') + ->willReturn([ + new MiddlewareRegistration('otherapp', 'foo', true), + new MiddlewareRegistration('otherapp', 'bar', false), + ]); + $this->container['foo'] = new class extends Middleware { + }; + $this->container['bar'] = new class extends Middleware { + }; + $coordinator->method('getRegistrationContext')->willReturn($registrationContext); + + $dispatcher = $this->container['MiddlewareDispatcher']; + + $middlewares = $dispatcher->getMiddlewares(); + self::assertNotEmpty($middlewares); + foreach ($middlewares as $middleware) { + if ($middleware === $this->container['bar']) { + $this->fail('Container must not register this middleware'); + } + if ($middleware === $this->container['foo']) { + // It is done + return; + } + } + $this->fail('Bootstrap registered middleware not found'); + } + + public function testInvalidAppClass(): void { $this->expectException(QueryException::class); $this->container->query('\OCA\Name\Foo'); } diff --git a/tests/lib/AppFramework/DependencyInjection/DIIntergrationTests.php b/tests/lib/AppFramework/DependencyInjection/DIIntergrationTests.php index 78a1d40bea6..219fd5134ae 100644 --- a/tests/lib/AppFramework/DependencyInjection/DIIntergrationTests.php +++ b/tests/lib/AppFramework/DependencyInjection/DIIntergrationTests.php @@ -1,24 +1,8 @@ <?php + /** - * @copyright 2017, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\AppFramework\DependencyInjection; @@ -38,21 +22,18 @@ class ClassA2 implements Interface1 { } class ClassB { - /** @var Interface1 */ - public $interface1; - /** * ClassB constructor. * * @param Interface1 $interface1 */ - public function __construct(Interface1 $interface1) { - $this->interface1 = $interface1; + public function __construct( + public Interface1 $interface1, + ) { } } class DIIntergrationTests extends TestCase { - /** @var DIContainer */ private $container; @@ -66,7 +47,7 @@ class DIIntergrationTests extends TestCase { $this->container = new DIContainer('App1', [], $this->server); } - public function testInjectFromServer() { + public function testInjectFromServer(): void { $this->server->registerService(Interface1::class, function () { return new ClassA1(); }); @@ -82,7 +63,7 @@ class DIIntergrationTests extends TestCase { $this->assertSame(ClassA1::class, get_class($res->interface1)); } - public function testInjectDepFromServer() { + public function testInjectDepFromServer(): void { $this->server->registerService(Interface1::class, function () { return new ClassA1(); }); @@ -98,7 +79,7 @@ class DIIntergrationTests extends TestCase { $this->assertSame(ClassA1::class, get_class($res->interface1)); } - public function testOverwriteDepFromServer() { + public function testOverwriteDepFromServer(): void { $this->server->registerService(Interface1::class, function () { return new ClassA1(); }); @@ -118,7 +99,7 @@ class DIIntergrationTests extends TestCase { $this->assertSame(ClassA2::class, get_class($res->interface1)); } - public function testIgnoreOverwriteInServerClass() { + public function testIgnoreOverwriteInServerClass(): void { $this->server->registerService(Interface1::class, function () { return new ClassA1(); }); diff --git a/tests/lib/AppFramework/Http/ContentSecurityPolicyTest.php b/tests/lib/AppFramework/Http/ContentSecurityPolicyTest.php index a96cd4a163b..75527e7eaf8 100644 --- a/tests/lib/AppFramework/Http/ContentSecurityPolicyTest.php +++ b/tests/lib/AppFramework/Http/ContentSecurityPolicyTest.php @@ -1,9 +1,9 @@ <?php + /** - * Copyright (c) 2015 Lukas Reschke lukas@owncloud.com - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\AppFramework\Http; @@ -16,7 +16,6 @@ use OCP\AppFramework\Http\ContentSecurityPolicy; * @package OC\AppFramework\Http */ class ContentSecurityPolicyTest extends \Test\TestCase { - /** @var ContentSecurityPolicy */ private $contentSecurityPolicy; @@ -25,416 +24,400 @@ class ContentSecurityPolicyTest extends \Test\TestCase { $this->contentSecurityPolicy = new ContentSecurityPolicy(); } - public function testGetPolicyDefault() { + public function testGetPolicyDefault(): void { $defaultPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; $this->assertSame($defaultPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyScriptDomainValid() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self' www.owncloud.com;style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; + public function testGetPolicyScriptDomainValid(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self' www.nextcloud.com;style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedScriptDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyScriptDomainValidMultiple() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self' www.owncloud.com www.owncloud.org;style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; + public function testGetPolicyScriptDomainValidMultiple(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self' www.nextcloud.com www.nextcloud.org;style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.com'); - $this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.org'); + $this->contentSecurityPolicy->addAllowedScriptDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->addAllowedScriptDomain('www.nextcloud.org'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowScriptDomain() { + public function testGetPolicyDisallowScriptDomain(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowScriptDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedScriptDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowScriptDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowScriptDomainMultiple() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self' www.owncloud.com;style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; + public function testGetPolicyDisallowScriptDomainMultiple(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self' www.nextcloud.com;style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowScriptDomain('www.owncloud.org'); + $this->contentSecurityPolicy->addAllowedScriptDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowScriptDomain('www.nextcloud.org'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowScriptDomainMultipleStacked() { + public function testGetPolicyDisallowScriptDomainMultipleStacked(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowScriptDomain('www.owncloud.org')->disallowScriptDomain('www.owncloud.com'); - $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); - } - - public function testGetPolicyScriptAllowInline() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self' 'unsafe-inline';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; - - $this->contentSecurityPolicy->allowInlineScript(true); - $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); - } - - public function testGetPolicyScriptAllowInlineWithDomain() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self' www.owncloud.com 'unsafe-inline';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; - - $this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.com'); - $this->contentSecurityPolicy->allowInlineScript(true); + $this->contentSecurityPolicy->addAllowedScriptDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowScriptDomain('www.nextcloud.org')->disallowScriptDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyScriptDisallowInlineAndEval() { + public function testGetPolicyScriptDisallowEval(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->allowInlineScript(false); $this->contentSecurityPolicy->allowEvalScript(false); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyStyleDomainValid() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' www.owncloud.com 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; + public function testGetPolicyStyleDomainValid(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' www.nextcloud.com 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedStyleDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyStyleDomainValidMultiple() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' www.owncloud.com www.owncloud.org 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; + public function testGetPolicyStyleDomainValidMultiple(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' www.nextcloud.com www.nextcloud.org 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.com'); - $this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.org'); + $this->contentSecurityPolicy->addAllowedStyleDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->addAllowedStyleDomain('www.nextcloud.org'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowStyleDomain() { + public function testGetPolicyDisallowStyleDomain(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowStyleDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedStyleDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowStyleDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowStyleDomainMultiple() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' www.owncloud.com 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; + public function testGetPolicyDisallowStyleDomainMultiple(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' www.nextcloud.com 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowStyleDomain('www.owncloud.org'); + $this->contentSecurityPolicy->addAllowedStyleDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowStyleDomain('www.nextcloud.org'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowStyleDomainMultipleStacked() { + public function testGetPolicyDisallowStyleDomainMultipleStacked(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowStyleDomain('www.owncloud.org')->disallowStyleDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedStyleDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowStyleDomain('www.nextcloud.org')->disallowStyleDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyStyleAllowInline() { + public function testGetPolicyStyleAllowInline(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; $this->contentSecurityPolicy->allowInlineStyle(true); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyStyleAllowInlineWithDomain() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' www.owncloud.com 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; + public function testGetPolicyStyleAllowInlineWithDomain(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' www.nextcloud.com 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedStyleDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyStyleDisallowInline() { + public function testGetPolicyStyleDisallowInline(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; $this->contentSecurityPolicy->allowInlineStyle(false); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyImageDomainValid() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob: www.owncloud.com;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; + public function testGetPolicyImageDomainValid(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob: www.nextcloud.com;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedImageDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedImageDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyImageDomainValidMultiple() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob: www.owncloud.com www.owncloud.org;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; + public function testGetPolicyImageDomainValidMultiple(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob: www.nextcloud.com www.nextcloud.org;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedImageDomain('www.owncloud.com'); - $this->contentSecurityPolicy->addAllowedImageDomain('www.owncloud.org'); + $this->contentSecurityPolicy->addAllowedImageDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->addAllowedImageDomain('www.nextcloud.org'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowImageDomain() { + public function testGetPolicyDisallowImageDomain(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedImageDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowImageDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedImageDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowImageDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowImageDomainMultiple() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob: www.owncloud.com;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; + public function testGetPolicyDisallowImageDomainMultiple(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob: www.nextcloud.com;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedImageDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowImageDomain('www.owncloud.org'); + $this->contentSecurityPolicy->addAllowedImageDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowImageDomain('www.nextcloud.org'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowImageDomainMultipleStakes() { + public function testGetPolicyDisallowImageDomainMultipleStakes(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedImageDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowImageDomain('www.owncloud.org')->disallowImageDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedImageDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowImageDomain('www.nextcloud.org')->disallowImageDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyFontDomainValid() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data: www.owncloud.com;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; + public function testGetPolicyFontDomainValid(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data: www.nextcloud.com;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedFontDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedFontDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyFontDomainValidMultiple() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data: www.owncloud.com www.owncloud.org;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; + public function testGetPolicyFontDomainValidMultiple(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data: www.nextcloud.com www.nextcloud.org;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedFontDomain('www.owncloud.com'); - $this->contentSecurityPolicy->addAllowedFontDomain('www.owncloud.org'); + $this->contentSecurityPolicy->addAllowedFontDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->addAllowedFontDomain('www.nextcloud.org'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowFontDomain() { + public function testGetPolicyDisallowFontDomain(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedFontDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowFontDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedFontDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowFontDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowFontDomainMultiple() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data: www.owncloud.com;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; + public function testGetPolicyDisallowFontDomainMultiple(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data: www.nextcloud.com;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedFontDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowFontDomain('www.owncloud.org'); + $this->contentSecurityPolicy->addAllowedFontDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowFontDomain('www.nextcloud.org'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowFontDomainMultipleStakes() { + public function testGetPolicyDisallowFontDomainMultipleStakes(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedFontDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowFontDomain('www.owncloud.org')->disallowFontDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedFontDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowFontDomain('www.nextcloud.org')->disallowFontDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyConnectDomainValid() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self' www.owncloud.com;media-src 'self';frame-ancestors 'self';form-action 'self'"; + public function testGetPolicyConnectDomainValid(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self' www.nextcloud.com;media-src 'self';frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedConnectDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedConnectDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyConnectDomainValidMultiple() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self' www.owncloud.com www.owncloud.org;media-src 'self';frame-ancestors 'self';form-action 'self'"; + public function testGetPolicyConnectDomainValidMultiple(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self' www.nextcloud.com www.nextcloud.org;media-src 'self';frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedConnectDomain('www.owncloud.com'); - $this->contentSecurityPolicy->addAllowedConnectDomain('www.owncloud.org'); + $this->contentSecurityPolicy->addAllowedConnectDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->addAllowedConnectDomain('www.nextcloud.org'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowConnectDomain() { + public function testGetPolicyDisallowConnectDomain(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedConnectDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowConnectDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedConnectDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowConnectDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowConnectDomainMultiple() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self' www.owncloud.com;media-src 'self';frame-ancestors 'self';form-action 'self'"; + public function testGetPolicyDisallowConnectDomainMultiple(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self' www.nextcloud.com;media-src 'self';frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedConnectDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowConnectDomain('www.owncloud.org'); + $this->contentSecurityPolicy->addAllowedConnectDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowConnectDomain('www.nextcloud.org'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowConnectDomainMultipleStakes() { + public function testGetPolicyDisallowConnectDomainMultipleStakes(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedConnectDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowConnectDomain('www.owncloud.org')->disallowConnectDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedConnectDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowConnectDomain('www.nextcloud.org')->disallowConnectDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyMediaDomainValid() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self' www.owncloud.com;frame-ancestors 'self';form-action 'self'"; + public function testGetPolicyMediaDomainValid(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self' www.nextcloud.com;frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedMediaDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedMediaDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyMediaDomainValidMultiple() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self' www.owncloud.com www.owncloud.org;frame-ancestors 'self';form-action 'self'"; + public function testGetPolicyMediaDomainValidMultiple(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self' www.nextcloud.com www.nextcloud.org;frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedMediaDomain('www.owncloud.com'); - $this->contentSecurityPolicy->addAllowedMediaDomain('www.owncloud.org'); + $this->contentSecurityPolicy->addAllowedMediaDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->addAllowedMediaDomain('www.nextcloud.org'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowMediaDomain() { + public function testGetPolicyDisallowMediaDomain(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedMediaDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowMediaDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedMediaDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowMediaDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowMediaDomainMultiple() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self' www.owncloud.com;frame-ancestors 'self';form-action 'self'"; + public function testGetPolicyDisallowMediaDomainMultiple(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self' www.nextcloud.com;frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedMediaDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowMediaDomain('www.owncloud.org'); + $this->contentSecurityPolicy->addAllowedMediaDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowMediaDomain('www.nextcloud.org'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowMediaDomainMultipleStakes() { + public function testGetPolicyDisallowMediaDomainMultipleStakes(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedMediaDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowMediaDomain('www.owncloud.org')->disallowMediaDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedMediaDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowMediaDomain('www.nextcloud.org')->disallowMediaDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyObjectDomainValid() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';object-src www.owncloud.com;frame-ancestors 'self';form-action 'self'"; + public function testGetPolicyObjectDomainValid(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';object-src www.nextcloud.com;frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedObjectDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedObjectDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyObjectDomainValidMultiple() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';object-src www.owncloud.com www.owncloud.org;frame-ancestors 'self';form-action 'self'"; + public function testGetPolicyObjectDomainValidMultiple(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';object-src www.nextcloud.com www.nextcloud.org;frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedObjectDomain('www.owncloud.com'); - $this->contentSecurityPolicy->addAllowedObjectDomain('www.owncloud.org'); + $this->contentSecurityPolicy->addAllowedObjectDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->addAllowedObjectDomain('www.nextcloud.org'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowObjectDomain() { + public function testGetPolicyDisallowObjectDomain(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedObjectDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowObjectDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedObjectDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowObjectDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowObjectDomainMultiple() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';object-src www.owncloud.com;frame-ancestors 'self';form-action 'self'"; + public function testGetPolicyDisallowObjectDomainMultiple(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';object-src www.nextcloud.com;frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedObjectDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowObjectDomain('www.owncloud.org'); + $this->contentSecurityPolicy->addAllowedObjectDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowObjectDomain('www.nextcloud.org'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowObjectDomainMultipleStakes() { + public function testGetPolicyDisallowObjectDomainMultipleStakes(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedObjectDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowObjectDomain('www.owncloud.org')->disallowObjectDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedObjectDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowObjectDomain('www.nextcloud.org')->disallowObjectDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetAllowedFrameDomain() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-src www.owncloud.com;frame-ancestors 'self';form-action 'self'"; + public function testGetAllowedFrameDomain(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-src www.nextcloud.com;frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedFrameDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedFrameDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyFrameDomainValidMultiple() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-src www.owncloud.com www.owncloud.org;frame-ancestors 'self';form-action 'self'"; + public function testGetPolicyFrameDomainValidMultiple(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-src www.nextcloud.com www.nextcloud.org;frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedFrameDomain('www.owncloud.com'); - $this->contentSecurityPolicy->addAllowedFrameDomain('www.owncloud.org'); + $this->contentSecurityPolicy->addAllowedFrameDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->addAllowedFrameDomain('www.nextcloud.org'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowFrameDomain() { + public function testGetPolicyDisallowFrameDomain(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedFrameDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowFrameDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedFrameDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowFrameDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowFrameDomainMultiple() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-src www.owncloud.com;frame-ancestors 'self';form-action 'self'"; + public function testGetPolicyDisallowFrameDomainMultiple(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-src www.nextcloud.com;frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedFrameDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowFrameDomain('www.owncloud.org'); + $this->contentSecurityPolicy->addAllowedFrameDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowFrameDomain('www.nextcloud.org'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowFrameDomainMultipleStakes() { + public function testGetPolicyDisallowFrameDomainMultipleStakes(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedFrameDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowFrameDomain('www.owncloud.org')->disallowFrameDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedFrameDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowFrameDomain('www.nextcloud.org')->disallowFrameDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetAllowedChildSrcDomain() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';child-src child.owncloud.com;frame-ancestors 'self';form-action 'self'"; + public function testGetAllowedChildSrcDomain(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';child-src child.nextcloud.com;frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedChildSrcDomain('child.owncloud.com'); + $this->contentSecurityPolicy->addAllowedChildSrcDomain('child.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyChildSrcValidMultiple() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';child-src child.owncloud.com child.owncloud.org;frame-ancestors 'self';form-action 'self'"; + public function testGetPolicyChildSrcValidMultiple(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';child-src child.nextcloud.com child.nextcloud.org;frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedChildSrcDomain('child.owncloud.com'); - $this->contentSecurityPolicy->addAllowedChildSrcDomain('child.owncloud.org'); + $this->contentSecurityPolicy->addAllowedChildSrcDomain('child.nextcloud.com'); + $this->contentSecurityPolicy->addAllowedChildSrcDomain('child.nextcloud.org'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowChildSrcDomain() { + public function testGetPolicyDisallowChildSrcDomain(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedChildSrcDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowChildSrcDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedChildSrcDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowChildSrcDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowChildSrcDomainMultiple() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';child-src www.owncloud.com;frame-ancestors 'self';form-action 'self'"; + public function testGetPolicyDisallowChildSrcDomainMultiple(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';child-src www.nextcloud.com;frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedChildSrcDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowChildSrcDomain('www.owncloud.org'); + $this->contentSecurityPolicy->addAllowedChildSrcDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowChildSrcDomain('www.nextcloud.org'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowChildSrcDomainMultipleStakes() { + public function testGetPolicyDisallowChildSrcDomainMultipleStakes(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedChildSrcDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowChildSrcDomain('www.owncloud.org')->disallowChildSrcDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedChildSrcDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowChildSrcDomain('www.nextcloud.org')->disallowChildSrcDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetAllowedFrameAncestorDomain() { + public function testGetAllowedFrameAncestorDomain(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self' sub.nextcloud.com;form-action 'self'"; $this->contentSecurityPolicy->addAllowedFrameAncestorDomain('sub.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyFrameAncestorValidMultiple() { + public function testGetPolicyFrameAncestorValidMultiple(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self' sub.nextcloud.com foo.nextcloud.com;form-action 'self'"; $this->contentSecurityPolicy->addAllowedFrameAncestorDomain('sub.nextcloud.com'); @@ -442,7 +425,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowFrameAncestorDomain() { + public function testGetPolicyDisallowFrameAncestorDomain(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; $this->contentSecurityPolicy->addAllowedFrameAncestorDomain('www.nextcloud.com'); @@ -450,7 +433,7 @@ class ContentSecurityPolicyTest extends \Test\TestCase { $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowFrameAncestorDomainMultiple() { + public function testGetPolicyDisallowFrameAncestorDomainMultiple(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self' www.nextcloud.com;form-action 'self'"; $this->contentSecurityPolicy->addAllowedFrameAncestorDomain('www.nextcloud.com'); @@ -458,35 +441,76 @@ class ContentSecurityPolicyTest extends \Test\TestCase { $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowFrameAncestorDomainMultipleStakes() { + public function testGetPolicyDisallowFrameAncestorDomainMultipleStakes(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; - $this->contentSecurityPolicy->addAllowedChildSrcDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowChildSrcDomain('www.owncloud.org')->disallowChildSrcDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedChildSrcDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowChildSrcDomain('www.nextcloud.org')->disallowChildSrcDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyUnsafeEval() { + public function testGetPolicyUnsafeEval(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self' 'unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; $this->contentSecurityPolicy->allowEvalScript(true); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyNonce() { - $nonce = 'my-nonce'; - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'nonce-".base64_encode($nonce) . "';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; + public function testGetPolicyUnsafeWasmEval(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self' 'wasm-unsafe-eval';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; + + $this->contentSecurityPolicy->allowEvalWasm(true); + $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); + } + + public function testGetPolicyNonce(): void { + $nonce = base64_encode('my-nonce'); + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'nonce-$nonce';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; + + $this->contentSecurityPolicy->useJsNonce($nonce); + $this->contentSecurityPolicy->useStrictDynamicOnScripts(false); + $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); + } + + public function testGetPolicyNonceDefault(): void { + $nonce = base64_encode('my-nonce'); + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'nonce-$nonce';script-src-elem 'strict-dynamic' 'nonce-$nonce';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; $this->contentSecurityPolicy->useJsNonce($nonce); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyNonceStrictDynamic() { - $nonce = 'my-nonce'; - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'strict-dynamic' 'nonce-".base64_encode($nonce) . "';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; + public function testGetPolicyNonceStrictDynamic(): void { + $nonce = base64_encode('my-nonce'); + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'strict-dynamic' 'nonce-$nonce';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; $this->contentSecurityPolicy->useJsNonce($nonce); $this->contentSecurityPolicy->useStrictDynamic(true); + $this->contentSecurityPolicy->useStrictDynamicOnScripts(false); + $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); + } + + public function testGetPolicyNonceStrictDynamicDefault(): void { + $nonce = base64_encode('my-nonce'); + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'strict-dynamic' 'nonce-$nonce';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; + + $this->contentSecurityPolicy->useJsNonce($nonce); + $this->contentSecurityPolicy->useStrictDynamic(true); + $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); + } + + public function testGetPolicyStrictDynamicOnScriptsOff(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; + + $this->contentSecurityPolicy->useStrictDynamicOnScripts(false); + $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); + } + + public function testGetPolicyStrictDynamicAndStrictDynamicOnScripts(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';style-src 'self' 'unsafe-inline';img-src 'self' data: blob:;font-src 'self' data:;connect-src 'self';media-src 'self';frame-ancestors 'self';form-action 'self'"; + + $this->contentSecurityPolicy->useStrictDynamic(true); + $this->contentSecurityPolicy->useStrictDynamicOnScripts(true); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } } diff --git a/tests/lib/AppFramework/Http/DataResponseTest.php b/tests/lib/AppFramework/Http/DataResponseTest.php index f933b3102b7..e9a2c511140 100644 --- a/tests/lib/AppFramework/Http/DataResponseTest.php +++ b/tests/lib/AppFramework/Http/DataResponseTest.php @@ -1,24 +1,9 @@ <?php /** - * ownCloud - App Framework - * - * @author Bernhard Posselt - * @copyright 2014 Bernhard Posselt <dev@bernhard-posselt.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\AppFramework\Http; @@ -26,9 +11,9 @@ namespace Test\AppFramework\Http; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; use OCP\IRequest; +use OCP\Server; class DataResponseTest extends \Test\TestCase { - /** * @var DataResponse */ @@ -40,7 +25,7 @@ class DataResponseTest extends \Test\TestCase { } - public function testSetData() { + public function testSetData(): void { $params = ['hi', 'yo']; $this->response->setData($params); @@ -48,7 +33,7 @@ class DataResponseTest extends \Test\TestCase { } - public function testConstructorAllowsToSetData() { + public function testConstructorAllowsToSetData(): void { $data = ['hi']; $code = 300; $response = new DataResponse($data, $code); @@ -58,7 +43,7 @@ class DataResponseTest extends \Test\TestCase { } - public function testConstructorAllowsToSetHeaders() { + public function testConstructorAllowsToSetHeaders(): void { $data = ['hi']; $code = 300; $headers = ['test' => 'something']; @@ -68,8 +53,8 @@ class DataResponseTest extends \Test\TestCase { 'Cache-Control' => 'no-cache, no-store, must-revalidate', 'Content-Security-Policy' => "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'", 'Feature-Policy' => "autoplay 'none';camera 'none';fullscreen 'none';geolocation 'none';microphone 'none';payment 'none'", - 'X-Robots-Tag' => 'none', - 'X-Request-Id' => \OC::$server->get(IRequest::class)->getId(), + 'X-Robots-Tag' => 'noindex, nofollow', + 'X-Request-Id' => Server::get(IRequest::class)->getId(), ]; $expectedHeaders = array_merge($expectedHeaders, $headers); @@ -79,7 +64,7 @@ class DataResponseTest extends \Test\TestCase { } - public function testChainability() { + public function testChainability(): void { $params = ['hi', 'yo']; $this->response->setData($params) ->setStatus(Http::STATUS_NOT_FOUND); diff --git a/tests/lib/AppFramework/Http/DispatcherTest.php b/tests/lib/AppFramework/Http/DispatcherTest.php index e1d78082a2d..86c78e840e0 100644 --- a/tests/lib/AppFramework/Http/DispatcherTest.php +++ b/tests/lib/AppFramework/Http/DispatcherTest.php @@ -1,28 +1,14 @@ <?php /** - * ownCloud - App Framework - * - * @author Bernhard Posselt - * @copyright 2012 Bernhard Posselt <dev@bernhard-posselt.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\AppFramework\Http; +use OC\AppFramework\DependencyInjection\DIContainer; use OC\AppFramework\Http\Dispatcher; use OC\AppFramework\Http\Request; use OC\AppFramework\Middleware\MiddlewareDispatcher; @@ -31,18 +17,22 @@ use OCP\AppFramework\Controller; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Http\JSONResponse; +use OCP\AppFramework\Http\ParameterOutOfRangeException; use OCP\AppFramework\Http\Response; use OCP\Diagnostics\IEventLogger; use OCP\IConfig; +use OCP\IDBConnection; use OCP\IRequest; +use OCP\IRequestId; +use OCP\Server; use PHPUnit\Framework\MockObject\MockObject; +use Psr\Container\ContainerInterface; use Psr\Log\LoggerInterface; -use OCP\IRequestId; class TestController extends Controller { /** * @param string $appName - * @param \OCP\IRequest $request + * @param IRequest $request */ public function __construct($appName, $request) { parent::__construct($appName, $request); @@ -51,11 +41,12 @@ class TestController extends Controller { /** * @param int $int * @param bool $bool + * @param double $foo * @param int $test - * @param int $test2 + * @param integer $test2 * @return array */ - public function exec($int, $bool, $test = 4, $test2 = 1) { + public function exec($int, $bool, $foo, $test = 4, $test2 = 1) { $this->registerResponder('text', function ($in) { return new JSONResponse(['text' => $in]); }); @@ -75,6 +66,10 @@ class TestController extends Controller { 'text' => [$int, $bool, $test, $test2] ]); } + + public function test(): Response { + return new DataResponse(); + } } /** @@ -89,22 +84,24 @@ class DispatcherTest extends \Test\TestCase { /** @var Dispatcher */ private $dispatcher; private $controllerMethod; + /** @var Controller|MockObject */ + private $controller; private $response; - /** @var IRequest|MockObject */ + /** @var IRequest|MockObject */ private $request; private $lastModified; private $etag; - /** @var Http|MockObject */ + /** @var Http|MockObject */ private $http; private $reflector; - /** @var IConfig|MockObject */ + /** @var IConfig|MockObject */ private $config; - /** @var LoggerInterface|MockObject */ + /** @var LoggerInterface|MockObject */ private $logger; - /** - * @var IEventLogger|MockObject - */ + /** @var IEventLogger|MockObject */ private $eventLogger; + /** @var ContainerInterface|MockObject */ + private $container; protected function setUp(): void { parent::setUp(); @@ -113,33 +110,18 @@ class DispatcherTest extends \Test\TestCase { $this->config = $this->createMock(IConfig::class); $this->logger = $this->createMock(LoggerInterface::class); $this->eventLogger = $this->createMock(IEventLogger::class); - $app = $this->getMockBuilder( - 'OC\AppFramework\DependencyInjection\DIContainer') - ->disableOriginalConstructor() - ->getMock(); - $request = $this->getMockBuilder( - '\OC\AppFramework\Http\Request') - ->disableOriginalConstructor() - ->getMock(); - $this->http = $this->getMockBuilder( - \OC\AppFramework\Http::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->middlewareDispatcher = $this->getMockBuilder( - '\OC\AppFramework\Middleware\MiddlewareDispatcher') - ->disableOriginalConstructor() - ->getMock(); - $this->controller = $this->getMockBuilder( - '\OCP\AppFramework\Controller') - ->setMethods([$this->controllerMethod]) + $this->container = $this->createMock(ContainerInterface::class); + $app = $this->createMock(DIContainer::class); + $request = $this->createMock(Request::class); + $this->http = $this->createMock(\OC\AppFramework\Http::class); + + $this->middlewareDispatcher = $this->createMock(MiddlewareDispatcher::class); + $this->controller = $this->getMockBuilder(TestController::class) + ->onlyMethods([$this->controllerMethod]) ->setConstructorArgs([$app, $request]) ->getMock(); - $this->request = $this->getMockBuilder( - '\OC\AppFramework\Http\Request') - ->disableOriginalConstructor() - ->getMock(); + $this->request = $this->createMock(Request::class); $this->reflector = new ControllerMethodReflector(); @@ -149,9 +131,10 @@ class DispatcherTest extends \Test\TestCase { $this->reflector, $this->request, $this->config, - \OC::$server->getDatabaseConnection(), + Server::get(IDBConnection::class), $this->logger, - $this->eventLogger + $this->eventLogger, + $this->container, ); $this->response = $this->createMock(Response::class); @@ -174,7 +157,7 @@ class DispatcherTest extends \Test\TestCase { ->method('beforeController') ->with($this->equalTo($this->controller), $this->equalTo($this->controllerMethod)) - ->will($this->throwException($exception)); + ->willThrowException($exception); if ($catchEx) { $this->middlewareDispatcher->expects($this->once()) ->method('afterException') @@ -238,7 +221,7 @@ class DispatcherTest extends \Test\TestCase { } - public function testDispatcherReturnsArrayWith2Entries() { + public function testDispatcherReturnsArrayWith2Entries(): void { $this->setMiddlewareExpectations(''); $response = $this->dispatcher->dispatch($this->controller, $this->controllerMethod); @@ -248,7 +231,7 @@ class DispatcherTest extends \Test\TestCase { } - public function testHeadersAndOutputAreReturned() { + public function testHeadersAndOutputAreReturned(): void { $out = 'yo'; $httpHeaders = 'Http'; $responseHeaders = ['hell' => 'yeah']; @@ -263,7 +246,7 @@ class DispatcherTest extends \Test\TestCase { } - public function testExceptionCallsAfterException() { + public function testExceptionCallsAfterException(): void { $out = 'yo'; $httpHeaders = 'Http'; $responseHeaders = ['hell' => 'yeah']; @@ -278,7 +261,7 @@ class DispatcherTest extends \Test\TestCase { } - public function testExceptionThrowsIfCanNotBeHandledByAfterException() { + public function testExceptionThrowsIfCanNotBeHandledByAfterException(): void { $out = 'yo'; $httpHeaders = 'Http'; $responseHeaders = ['hell' => 'yeah']; @@ -294,7 +277,7 @@ class DispatcherTest extends \Test\TestCase { private function dispatcherPassthrough() { $this->middlewareDispatcher->expects($this->once()) - ->method('beforeController'); + ->method('beforeController'); $this->middlewareDispatcher->expects($this->once()) ->method('afterController') ->willReturnCallback(function ($a, $b, $in) { @@ -308,12 +291,13 @@ class DispatcherTest extends \Test\TestCase { } - public function testControllerParametersInjected() { + public function testControllerParametersInjected(): void { $this->request = new Request( [ 'post' => [ 'int' => '3', - 'bool' => 'false' + 'bool' => 'false', + 'double' => 1.2, ], 'method' => 'POST' ], @@ -324,9 +308,10 @@ class DispatcherTest extends \Test\TestCase { $this->http, $this->middlewareDispatcher, $this->reflector, $this->request, $this->config, - \OC::$server->getDatabaseConnection(), + Server::get(IDBConnection::class), $this->logger, - $this->eventLogger + $this->eventLogger, + $this->container ); $controller = new TestController('app', $this->request); @@ -334,16 +319,17 @@ class DispatcherTest extends \Test\TestCase { $this->dispatcherPassthrough(); $response = $this->dispatcher->dispatch($controller, 'exec'); - $this->assertEquals('[3,true,4,1]', $response[3]); + $this->assertEquals('[3,false,4,1]', $response[3]); } - public function testControllerParametersInjectedDefaultOverwritten() { + public function testControllerParametersInjectedDefaultOverwritten(): void { $this->request = new Request( [ 'post' => [ 'int' => '3', 'bool' => 'false', + 'double' => 1.2, 'test2' => 7 ], 'method' => 'POST', @@ -355,9 +341,10 @@ class DispatcherTest extends \Test\TestCase { $this->http, $this->middlewareDispatcher, $this->reflector, $this->request, $this->config, - \OC::$server->getDatabaseConnection(), + Server::get(IDBConnection::class), $this->logger, - $this->eventLogger + $this->eventLogger, + $this->container ); $controller = new TestController('app', $this->request); @@ -365,17 +352,18 @@ class DispatcherTest extends \Test\TestCase { $this->dispatcherPassthrough(); $response = $this->dispatcher->dispatch($controller, 'exec'); - $this->assertEquals('[3,true,4,7]', $response[3]); + $this->assertEquals('[3,false,4,7]', $response[3]); } - public function testResponseTransformedByUrlFormat() { + public function testResponseTransformedByUrlFormat(): void { $this->request = new Request( [ 'post' => [ 'int' => '3', - 'bool' => 'false' + 'bool' => 'false', + 'double' => 1.2, ], 'urlParams' => [ 'format' => 'text' @@ -389,9 +377,10 @@ class DispatcherTest extends \Test\TestCase { $this->http, $this->middlewareDispatcher, $this->reflector, $this->request, $this->config, - \OC::$server->getDatabaseConnection(), + Server::get(IDBConnection::class), $this->logger, - $this->eventLogger + $this->eventLogger, + $this->container ); $controller = new TestController('app', $this->request); @@ -403,12 +392,13 @@ class DispatcherTest extends \Test\TestCase { } - public function testResponseTransformsDataResponse() { + public function testResponseTransformsDataResponse(): void { $this->request = new Request( [ 'post' => [ 'int' => '3', - 'bool' => 'false' + 'bool' => 'false', + 'double' => 1.2, ], 'urlParams' => [ 'format' => 'json' @@ -422,9 +412,10 @@ class DispatcherTest extends \Test\TestCase { $this->http, $this->middlewareDispatcher, $this->reflector, $this->request, $this->config, - \OC::$server->getDatabaseConnection(), + Server::get(IDBConnection::class), $this->logger, - $this->eventLogger + $this->eventLogger, + $this->container ); $controller = new TestController('app', $this->request); @@ -436,12 +427,13 @@ class DispatcherTest extends \Test\TestCase { } - public function testResponseTransformedByAcceptHeader() { + public function testResponseTransformedByAcceptHeader(): void { $this->request = new Request( [ 'post' => [ 'int' => '3', - 'bool' => 'false' + 'bool' => 'false', + 'double' => 1.2, ], 'server' => [ 'HTTP_ACCEPT' => 'application/text, test', @@ -456,9 +448,10 @@ class DispatcherTest extends \Test\TestCase { $this->http, $this->middlewareDispatcher, $this->reflector, $this->request, $this->config, - \OC::$server->getDatabaseConnection(), + Server::get(IDBConnection::class), $this->logger, - $this->eventLogger + $this->eventLogger, + $this->container ); $controller = new TestController('app', $this->request); @@ -469,13 +462,49 @@ class DispatcherTest extends \Test\TestCase { $this->assertEquals('{"text":[3,false,4,1]}', $response[3]); } + public function testResponseTransformedBySendingMultipartFormData(): void { + $this->request = new Request( + [ + 'post' => [ + 'int' => '3', + 'bool' => 'false', + 'double' => 1.2, + ], + 'server' => [ + 'HTTP_ACCEPT' => 'application/text, test', + 'HTTP_CONTENT_TYPE' => 'multipart/form-data' + ], + 'method' => 'POST' + ], + $this->createMock(IRequestId::class), + $this->createMock(IConfig::class) + ); + $this->dispatcher = new Dispatcher( + $this->http, $this->middlewareDispatcher, $this->reflector, + $this->request, + $this->config, + Server::get(IDBConnection::class), + $this->logger, + $this->eventLogger, + $this->container + ); + $controller = new TestController('app', $this->request); + + // reflector is supposed to be called once + $this->dispatcherPassthrough(); + $response = $this->dispatcher->dispatch($controller, 'exec'); + + $this->assertEquals('{"text":[3,false,4,1]}', $response[3]); + } - public function testResponsePrimarilyTransformedByParameterFormat() { + + public function testResponsePrimarilyTransformedByParameterFormat(): void { $this->request = new Request( [ 'post' => [ 'int' => '3', - 'bool' => 'false' + 'bool' => 'false', + 'double' => 1.2, ], 'get' => [ 'format' => 'text' @@ -492,9 +521,10 @@ class DispatcherTest extends \Test\TestCase { $this->http, $this->middlewareDispatcher, $this->reflector, $this->request, $this->config, - \OC::$server->getDatabaseConnection(), + Server::get(IDBConnection::class), $this->logger, - $this->eventLogger + $this->eventLogger, + $this->container ); $controller = new TestController('app', $this->request); @@ -502,6 +532,51 @@ class DispatcherTest extends \Test\TestCase { $this->dispatcherPassthrough(); $response = $this->dispatcher->dispatch($controller, 'exec'); - $this->assertEquals('{"text":[3,true,4,1]}', $response[3]); + $this->assertEquals('{"text":[3,false,4,1]}', $response[3]); + } + + + public static function rangeDataProvider(): array { + return [ + [PHP_INT_MIN, PHP_INT_MAX, 42, false], + [0, 12, -5, true], + [-12, 0, 5, true], + [7, 14, 5, true], + [7, 14, 10, false], + [-14, -7, -10, false], + ]; + } + + #[\PHPUnit\Framework\Attributes\DataProvider('rangeDataProvider')] + public function testEnsureParameterValueSatisfiesRange(int $min, int $max, int $input, bool $throw): void { + $this->reflector = $this->createMock(ControllerMethodReflector::class); + $this->reflector->expects($this->any()) + ->method('getRange') + ->willReturn([ + 'min' => $min, + 'max' => $max, + ]); + + $this->dispatcher = new Dispatcher( + $this->http, + $this->middlewareDispatcher, + $this->reflector, + $this->request, + $this->config, + Server::get(IDBConnection::class), + $this->logger, + $this->eventLogger, + $this->container, + ); + + if ($throw) { + $this->expectException(ParameterOutOfRangeException::class); + } + + $this->invokePrivate($this->dispatcher, 'ensureParameterValueSatisfiesRange', ['myArgument', $input]); + if (!$throw) { + // do not mark this test risky + $this->assertTrue(true); + } } } diff --git a/tests/lib/AppFramework/Http/DownloadResponseTest.php b/tests/lib/AppFramework/Http/DownloadResponseTest.php index 89de248cea0..b2f60edd999 100644 --- a/tests/lib/AppFramework/Http/DownloadResponseTest.php +++ b/tests/lib/AppFramework/Http/DownloadResponseTest.php @@ -1,24 +1,9 @@ <?php /** - * ownCloud - App Framework - * - * @author Bernhard Posselt - * @copyright 2012 Bernhard Posselt <dev@bernhard-posselt.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\AppFramework\Http; @@ -34,7 +19,7 @@ class DownloadResponseTest extends \Test\TestCase { parent::setUp(); } - public function testHeaders() { + public function testHeaders(): void { $response = new ChildDownloadResponse('file', 'content'); $headers = $response->getHeaders(); @@ -42,17 +27,15 @@ class DownloadResponseTest extends \Test\TestCase { $this->assertEquals('content', $headers['Content-Type']); } - /** - * @dataProvider filenameEncodingProvider - */ - public function testFilenameEncoding(string $input, string $expected) { + #[\PHPUnit\Framework\Attributes\DataProvider('filenameEncodingProvider')] + public function testFilenameEncoding(string $input, string $expected): void { $response = new ChildDownloadResponse($input, 'content'); $headers = $response->getHeaders(); - $this->assertEquals('attachment; filename="'.$expected.'"', $headers['Content-Disposition']); + $this->assertEquals('attachment; filename="' . $expected . '"', $headers['Content-Disposition']); } - public function filenameEncodingProvider() : array { + public static function filenameEncodingProvider() : array { return [ ['TestName.txt', 'TestName.txt'], ['A "Quoted" Filename.txt', 'A \\"Quoted\\" Filename.txt'], diff --git a/tests/lib/AppFramework/Http/EmptyContentSecurityPolicyTest.php b/tests/lib/AppFramework/Http/EmptyContentSecurityPolicyTest.php index 5fe81bb0136..66abce43cc4 100644 --- a/tests/lib/AppFramework/Http/EmptyContentSecurityPolicyTest.php +++ b/tests/lib/AppFramework/Http/EmptyContentSecurityPolicyTest.php @@ -1,9 +1,9 @@ <?php + /** - * Copyright (c) 2015 Lukas Reschke lukas@owncloud.com - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\AppFramework\Http; @@ -16,7 +16,6 @@ use OCP\AppFramework\Http\EmptyContentSecurityPolicy; * @package OC\AppFramework\Http */ class EmptyContentSecurityPolicyTest extends \Test\TestCase { - /** @var EmptyContentSecurityPolicy */ private $contentSecurityPolicy; @@ -25,443 +24,475 @@ class EmptyContentSecurityPolicyTest extends \Test\TestCase { $this->contentSecurityPolicy = new EmptyContentSecurityPolicy(); } - public function testGetPolicyDefault() { + public function testGetPolicyDefault(): void { $defaultPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'"; $this->assertSame($defaultPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyScriptDomainValid() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src www.owncloud.com;frame-ancestors 'none'"; + public function testGetPolicyScriptDomainValid(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src www.nextcloud.com;frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedScriptDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyScriptDomainValidMultiple() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src www.owncloud.com www.owncloud.org;frame-ancestors 'none'"; + public function testGetPolicyScriptDomainValidMultiple(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src www.nextcloud.com www.nextcloud.org;frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.com'); - $this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.org'); + $this->contentSecurityPolicy->addAllowedScriptDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->addAllowedScriptDomain('www.nextcloud.org'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowScriptDomain() { + public function testGetPolicyDisallowScriptDomain(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowScriptDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedScriptDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowScriptDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowScriptDomainMultiple() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src www.owncloud.com;frame-ancestors 'none'"; + public function testGetPolicyDisallowScriptDomainMultiple(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src www.nextcloud.com;frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowScriptDomain('www.owncloud.org'); + $this->contentSecurityPolicy->addAllowedScriptDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowScriptDomain('www.nextcloud.org'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowScriptDomainMultipleStacked() { + public function testGetPolicyDisallowScriptDomainMultipleStacked(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowScriptDomain('www.owncloud.org')->disallowScriptDomain('www.owncloud.com'); - $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); - } - - public function testGetPolicyScriptAllowInline() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'unsafe-inline';frame-ancestors 'none'"; - - $this->contentSecurityPolicy->allowInlineScript(true); + $this->contentSecurityPolicy->addAllowedScriptDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowScriptDomain('www.nextcloud.org')->disallowScriptDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyScriptAllowInlineWithDomain() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src www.owncloud.com 'unsafe-inline';frame-ancestors 'none'"; + public function testGetPolicyScriptAllowEval(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'unsafe-eval';frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.com'); - $this->contentSecurityPolicy->allowInlineScript(true); + $this->contentSecurityPolicy->allowEvalScript(true); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyScriptAllowInlineAndEval() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'unsafe-inline' 'unsafe-eval';frame-ancestors 'none'"; + public function testGetPolicyScriptAllowWasmEval(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'wasm-unsafe-eval';frame-ancestors 'none'"; - $this->contentSecurityPolicy->allowInlineScript(true); - $this->contentSecurityPolicy->allowEvalScript(true); + $this->contentSecurityPolicy->allowEvalWasm(true); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyStyleDomainValid() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';style-src www.owncloud.com;frame-ancestors 'none'"; + public function testGetPolicyStyleDomainValid(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';style-src www.nextcloud.com;frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedStyleDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyStyleDomainValidMultiple() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';style-src www.owncloud.com www.owncloud.org;frame-ancestors 'none'"; + public function testGetPolicyStyleDomainValidMultiple(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';style-src www.nextcloud.com www.nextcloud.org;frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.com'); - $this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.org'); + $this->contentSecurityPolicy->addAllowedStyleDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->addAllowedStyleDomain('www.nextcloud.org'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowStyleDomain() { + public function testGetPolicyDisallowStyleDomain(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowStyleDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedStyleDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowStyleDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowStyleDomainMultiple() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';style-src www.owncloud.com;frame-ancestors 'none'"; + public function testGetPolicyDisallowStyleDomainMultiple(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';style-src www.nextcloud.com;frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowStyleDomain('www.owncloud.org'); + $this->contentSecurityPolicy->addAllowedStyleDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowStyleDomain('www.nextcloud.org'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowStyleDomainMultipleStacked() { + public function testGetPolicyDisallowStyleDomainMultipleStacked(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowStyleDomain('www.owncloud.org')->disallowStyleDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedStyleDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowStyleDomain('www.nextcloud.org')->disallowStyleDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyStyleAllowInline() { + public function testGetPolicyStyleAllowInline(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';style-src 'unsafe-inline';frame-ancestors 'none'"; $this->contentSecurityPolicy->allowInlineStyle(true); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyStyleAllowInlineWithDomain() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';style-src www.owncloud.com 'unsafe-inline';frame-ancestors 'none'"; + public function testGetPolicyStyleAllowInlineWithDomain(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';style-src www.nextcloud.com 'unsafe-inline';frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedStyleDomain('www.nextcloud.com'); $this->contentSecurityPolicy->allowInlineStyle(true); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyStyleDisallowInline() { + public function testGetPolicyStyleDisallowInline(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'"; $this->contentSecurityPolicy->allowInlineStyle(false); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyImageDomainValid() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';img-src www.owncloud.com;frame-ancestors 'none'"; + public function testGetPolicyImageDomainValid(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';img-src www.nextcloud.com;frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedImageDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedImageDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyImageDomainValidMultiple() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';img-src www.owncloud.com www.owncloud.org;frame-ancestors 'none'"; + public function testGetPolicyImageDomainValidMultiple(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';img-src www.nextcloud.com www.nextcloud.org;frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedImageDomain('www.owncloud.com'); - $this->contentSecurityPolicy->addAllowedImageDomain('www.owncloud.org'); + $this->contentSecurityPolicy->addAllowedImageDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->addAllowedImageDomain('www.nextcloud.org'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowImageDomain() { + public function testGetPolicyDisallowImageDomain(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedImageDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowImageDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedImageDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowImageDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowImageDomainMultiple() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';img-src www.owncloud.com;frame-ancestors 'none'"; + public function testGetPolicyDisallowImageDomainMultiple(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';img-src www.nextcloud.com;frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedImageDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowImageDomain('www.owncloud.org'); + $this->contentSecurityPolicy->addAllowedImageDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowImageDomain('www.nextcloud.org'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowImageDomainMultipleStakes() { + public function testGetPolicyDisallowImageDomainMultipleStakes(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedImageDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowImageDomain('www.owncloud.org')->disallowImageDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedImageDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowImageDomain('www.nextcloud.org')->disallowImageDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyFontDomainValid() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';font-src www.owncloud.com;frame-ancestors 'none'"; + public function testGetPolicyFontDomainValid(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';font-src www.nextcloud.com;frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedFontDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedFontDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyFontDomainValidMultiple() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';font-src www.owncloud.com www.owncloud.org;frame-ancestors 'none'"; + public function testGetPolicyFontDomainValidMultiple(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';font-src www.nextcloud.com www.nextcloud.org;frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedFontDomain('www.owncloud.com'); - $this->contentSecurityPolicy->addAllowedFontDomain('www.owncloud.org'); + $this->contentSecurityPolicy->addAllowedFontDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->addAllowedFontDomain('www.nextcloud.org'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowFontDomain() { + public function testGetPolicyDisallowFontDomain(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedFontDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowFontDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedFontDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowFontDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowFontDomainMultiple() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';font-src www.owncloud.com;frame-ancestors 'none'"; + public function testGetPolicyDisallowFontDomainMultiple(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';font-src www.nextcloud.com;frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedFontDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowFontDomain('www.owncloud.org'); + $this->contentSecurityPolicy->addAllowedFontDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowFontDomain('www.nextcloud.org'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowFontDomainMultipleStakes() { + public function testGetPolicyDisallowFontDomainMultipleStakes(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedFontDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowFontDomain('www.owncloud.org')->disallowFontDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedFontDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowFontDomain('www.nextcloud.org')->disallowFontDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyConnectDomainValid() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';connect-src www.owncloud.com;frame-ancestors 'none'"; + public function testGetPolicyConnectDomainValid(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';connect-src www.nextcloud.com;frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedConnectDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedConnectDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyConnectDomainValidMultiple() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';connect-src www.owncloud.com www.owncloud.org;frame-ancestors 'none'"; + public function testGetPolicyConnectDomainValidMultiple(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';connect-src www.nextcloud.com www.nextcloud.org;frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedConnectDomain('www.owncloud.com'); - $this->contentSecurityPolicy->addAllowedConnectDomain('www.owncloud.org'); + $this->contentSecurityPolicy->addAllowedConnectDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->addAllowedConnectDomain('www.nextcloud.org'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowConnectDomain() { + public function testGetPolicyDisallowConnectDomain(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedConnectDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowConnectDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedConnectDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowConnectDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowConnectDomainMultiple() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';connect-src www.owncloud.com;frame-ancestors 'none'"; + public function testGetPolicyDisallowConnectDomainMultiple(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';connect-src www.nextcloud.com;frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedConnectDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowConnectDomain('www.owncloud.org'); + $this->contentSecurityPolicy->addAllowedConnectDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowConnectDomain('www.nextcloud.org'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowConnectDomainMultipleStakes() { + public function testGetPolicyDisallowConnectDomainMultipleStakes(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedConnectDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowConnectDomain('www.owncloud.org')->disallowConnectDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedConnectDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowConnectDomain('www.nextcloud.org')->disallowConnectDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyMediaDomainValid() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';media-src www.owncloud.com;frame-ancestors 'none'"; + public function testGetPolicyMediaDomainValid(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';media-src www.nextcloud.com;frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedMediaDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedMediaDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyMediaDomainValidMultiple() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';media-src www.owncloud.com www.owncloud.org;frame-ancestors 'none'"; + public function testGetPolicyMediaDomainValidMultiple(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';media-src www.nextcloud.com www.nextcloud.org;frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedMediaDomain('www.owncloud.com'); - $this->contentSecurityPolicy->addAllowedMediaDomain('www.owncloud.org'); + $this->contentSecurityPolicy->addAllowedMediaDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->addAllowedMediaDomain('www.nextcloud.org'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowMediaDomain() { + public function testGetPolicyDisallowMediaDomain(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedMediaDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowMediaDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedMediaDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowMediaDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowMediaDomainMultiple() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';media-src www.owncloud.com;frame-ancestors 'none'"; + public function testGetPolicyDisallowMediaDomainMultiple(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';media-src www.nextcloud.com;frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedMediaDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowMediaDomain('www.owncloud.org'); + $this->contentSecurityPolicy->addAllowedMediaDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowMediaDomain('www.nextcloud.org'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowMediaDomainMultipleStakes() { + public function testGetPolicyDisallowMediaDomainMultipleStakes(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedMediaDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowMediaDomain('www.owncloud.org')->disallowMediaDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedMediaDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowMediaDomain('www.nextcloud.org')->disallowMediaDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyObjectDomainValid() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';object-src www.owncloud.com;frame-ancestors 'none'"; + public function testGetPolicyObjectDomainValid(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';object-src www.nextcloud.com;frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedObjectDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedObjectDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyObjectDomainValidMultiple() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';object-src www.owncloud.com www.owncloud.org;frame-ancestors 'none'"; + public function testGetPolicyObjectDomainValidMultiple(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';object-src www.nextcloud.com www.nextcloud.org;frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedObjectDomain('www.owncloud.com'); - $this->contentSecurityPolicy->addAllowedObjectDomain('www.owncloud.org'); + $this->contentSecurityPolicy->addAllowedObjectDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->addAllowedObjectDomain('www.nextcloud.org'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowObjectDomain() { + public function testGetPolicyDisallowObjectDomain(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedObjectDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowObjectDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedObjectDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowObjectDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowObjectDomainMultiple() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';object-src www.owncloud.com;frame-ancestors 'none'"; + public function testGetPolicyDisallowObjectDomainMultiple(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';object-src www.nextcloud.com;frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedObjectDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowObjectDomain('www.owncloud.org'); + $this->contentSecurityPolicy->addAllowedObjectDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowObjectDomain('www.nextcloud.org'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowObjectDomainMultipleStakes() { + public function testGetPolicyDisallowObjectDomainMultipleStakes(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedObjectDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowObjectDomain('www.owncloud.org')->disallowObjectDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedObjectDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowObjectDomain('www.nextcloud.org')->disallowObjectDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetAllowedFrameDomain() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-src www.owncloud.com;frame-ancestors 'none'"; + public function testGetAllowedFrameDomain(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-src www.nextcloud.com;frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedFrameDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedFrameDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyFrameDomainValidMultiple() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-src www.owncloud.com www.owncloud.org;frame-ancestors 'none'"; + public function testGetPolicyFrameDomainValidMultiple(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-src www.nextcloud.com www.nextcloud.org;frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedFrameDomain('www.owncloud.com'); - $this->contentSecurityPolicy->addAllowedFrameDomain('www.owncloud.org'); + $this->contentSecurityPolicy->addAllowedFrameDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->addAllowedFrameDomain('www.nextcloud.org'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowFrameDomain() { + public function testGetPolicyDisallowFrameDomain(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedFrameDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowFrameDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedFrameDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowFrameDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowFrameDomainMultiple() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-src www.owncloud.com;frame-ancestors 'none'"; + public function testGetPolicyDisallowFrameDomainMultiple(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-src www.nextcloud.com;frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedFrameDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowFrameDomain('www.owncloud.org'); + $this->contentSecurityPolicy->addAllowedFrameDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowFrameDomain('www.nextcloud.org'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowFrameDomainMultipleStakes() { + public function testGetPolicyDisallowFrameDomainMultipleStakes(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedFrameDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowFrameDomain('www.owncloud.org')->disallowFrameDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedFrameDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowFrameDomain('www.nextcloud.org')->disallowFrameDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetAllowedChildSrcDomain() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';child-src child.owncloud.com;frame-ancestors 'none'"; + public function testGetAllowedChildSrcDomain(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';child-src child.nextcloud.com;frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedChildSrcDomain('child.owncloud.com'); + $this->contentSecurityPolicy->addAllowedChildSrcDomain('child.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyChildSrcValidMultiple() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';child-src child.owncloud.com child.owncloud.org;frame-ancestors 'none'"; + public function testGetPolicyChildSrcValidMultiple(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';child-src child.nextcloud.com child.nextcloud.org;frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedChildSrcDomain('child.owncloud.com'); - $this->contentSecurityPolicy->addAllowedChildSrcDomain('child.owncloud.org'); + $this->contentSecurityPolicy->addAllowedChildSrcDomain('child.nextcloud.com'); + $this->contentSecurityPolicy->addAllowedChildSrcDomain('child.nextcloud.org'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowChildSrcDomain() { + public function testGetPolicyDisallowChildSrcDomain(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedChildSrcDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowChildSrcDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedChildSrcDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowChildSrcDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowChildSrcDomainMultiple() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';child-src www.owncloud.com;frame-ancestors 'none'"; + public function testGetPolicyDisallowChildSrcDomainMultiple(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';child-src www.nextcloud.com;frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedChildSrcDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowChildSrcDomain('www.owncloud.org'); + $this->contentSecurityPolicy->addAllowedChildSrcDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowChildSrcDomain('www.nextcloud.org'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyDisallowChildSrcDomainMultipleStakes() { + public function testGetPolicyDisallowChildSrcDomainMultipleStakes(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'"; - $this->contentSecurityPolicy->addAllowedChildSrcDomain('www.owncloud.com'); - $this->contentSecurityPolicy->disallowChildSrcDomain('www.owncloud.org')->disallowChildSrcDomain('www.owncloud.com'); + $this->contentSecurityPolicy->addAllowedChildSrcDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->disallowChildSrcDomain('www.nextcloud.org')->disallowChildSrcDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyWithJsNonceAndScriptDomains() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'nonce-TXlKc05vbmNl' www.nextcloud.com www.nextcloud.org;frame-ancestors 'none'"; + public function testGetPolicyWithJsNonceAndScriptDomains(): void { + $nonce = base64_encode('MyJsNonce'); + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'nonce-$nonce' www.nextcloud.com www.nextcloud.org;frame-ancestors 'none'"; $this->contentSecurityPolicy->addAllowedScriptDomain('www.nextcloud.com'); - $this->contentSecurityPolicy->useJsNonce('MyJsNonce'); + $this->contentSecurityPolicy->useJsNonce($nonce); $this->contentSecurityPolicy->addAllowedScriptDomain('www.nextcloud.org'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyWithJsNonceAndSelfScriptDomain() { - $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'nonce-TXlKc05vbmNl';frame-ancestors 'none'"; + public function testGetPolicyWithJsNonceAndStrictDynamic(): void { + $nonce = base64_encode('MyJsNonce'); + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'strict-dynamic' 'nonce-$nonce' www.nextcloud.com;frame-ancestors 'none'"; + + $this->contentSecurityPolicy->addAllowedScriptDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->useStrictDynamic(true); + $this->contentSecurityPolicy->useJsNonce($nonce); + $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); + } + + public function testGetPolicyWithJsNonceAndStrictDynamicAndStrictDynamicOnScripts(): void { + $nonce = base64_encode('MyJsNonce'); + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'strict-dynamic' 'nonce-$nonce' www.nextcloud.com;frame-ancestors 'none'"; + + $this->contentSecurityPolicy->addAllowedScriptDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->useStrictDynamic(true); + $this->contentSecurityPolicy->useStrictDynamicOnScripts(true); + $this->contentSecurityPolicy->useJsNonce($nonce); + // Should be same as `testGetPolicyWithJsNonceAndStrictDynamic` because of fallback + $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); + } + + public function testGetPolicyWithJsNonceAndStrictDynamicOnScripts(): void { + $nonce = base64_encode('MyJsNonce'); + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'nonce-$nonce' www.nextcloud.com;script-src-elem 'strict-dynamic' 'nonce-$nonce' www.nextcloud.com;frame-ancestors 'none'"; + + $this->contentSecurityPolicy->addAllowedScriptDomain('www.nextcloud.com'); + $this->contentSecurityPolicy->useStrictDynamicOnScripts(true); + $this->contentSecurityPolicy->useJsNonce($nonce); + $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); + } + + public function testGetPolicyWithStrictDynamicOnScripts(): void { + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'"; + + $this->contentSecurityPolicy->useStrictDynamicOnScripts(true); + $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); + } + + public function testGetPolicyWithJsNonceAndSelfScriptDomain(): void { + $nonce = base64_encode('MyJsNonce'); + $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'nonce-$nonce';frame-ancestors 'none'"; - $this->contentSecurityPolicy->useJsNonce('MyJsNonce'); + $this->contentSecurityPolicy->useJsNonce($nonce); $this->contentSecurityPolicy->addAllowedScriptDomain("'self'"); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyWithoutJsNonceAndSelfScriptDomain() { + public function testGetPolicyWithoutJsNonceAndSelfScriptDomain(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self';frame-ancestors 'none'"; $this->contentSecurityPolicy->addAllowedScriptDomain("'self'"); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyWithReportUri() { + public function testGetPolicyWithReportUri(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none';report-uri https://my-report-uri.com"; - $this->contentSecurityPolicy->addReportTo("https://my-report-uri.com"); + $this->contentSecurityPolicy->addReportTo('https://my-report-uri.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } - public function testGetPolicyWithMultipleReportUri() { + public function testGetPolicyWithMultipleReportUri(): void { $expectedPolicy = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none';report-uri https://my-report-uri.com https://my-other-report-uri.com"; - $this->contentSecurityPolicy->addReportTo("https://my-report-uri.com"); - $this->contentSecurityPolicy->addReportTo("https://my-other-report-uri.com"); + $this->contentSecurityPolicy->addReportTo('https://my-report-uri.com'); + $this->contentSecurityPolicy->addReportTo('https://my-other-report-uri.com'); $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy()); } } diff --git a/tests/lib/AppFramework/Http/EmptyFeaturePolicyTest.php b/tests/lib/AppFramework/Http/EmptyFeaturePolicyTest.php index 18c255463c5..71342485552 100644 --- a/tests/lib/AppFramework/Http/EmptyFeaturePolicyTest.php +++ b/tests/lib/AppFramework/Http/EmptyFeaturePolicyTest.php @@ -2,25 +2,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\AppFramework\Http; @@ -28,7 +11,6 @@ namespace Test\AppFramework\Http; use OCP\AppFramework\Http\EmptyFeaturePolicy; class EmptyFeaturePolicyTest extends \Test\TestCase { - /** @var EmptyFeaturePolicy */ private $policy; @@ -37,19 +19,19 @@ class EmptyFeaturePolicyTest extends \Test\TestCase { $this->policy = new EmptyFeaturePolicy(); } - public function testGetPolicyDefault() { + public function testGetPolicyDefault(): void { $defaultPolicy = "autoplay 'none';camera 'none';fullscreen 'none';geolocation 'none';microphone 'none';payment 'none'"; $this->assertSame($defaultPolicy, $this->policy->buildPolicy()); } - public function testGetPolicyAutoplayDomainValid() { + public function testGetPolicyAutoplayDomainValid(): void { $expectedPolicy = "autoplay www.nextcloud.com;camera 'none';fullscreen 'none';geolocation 'none';microphone 'none';payment 'none'"; $this->policy->addAllowedAutoplayDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->policy->buildPolicy()); } - public function testGetPolicyAutoplayDomainValidMultiple() { + public function testGetPolicyAutoplayDomainValidMultiple(): void { $expectedPolicy = "autoplay www.nextcloud.com www.nextcloud.org;camera 'none';fullscreen 'none';geolocation 'none';microphone 'none';payment 'none'"; $this->policy->addAllowedAutoplayDomain('www.nextcloud.com'); @@ -57,14 +39,14 @@ class EmptyFeaturePolicyTest extends \Test\TestCase { $this->assertSame($expectedPolicy, $this->policy->buildPolicy()); } - public function testGetPolicyCameraDomainValid() { + public function testGetPolicyCameraDomainValid(): void { $expectedPolicy = "autoplay 'none';camera www.nextcloud.com;fullscreen 'none';geolocation 'none';microphone 'none';payment 'none'"; $this->policy->addAllowedCameraDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->policy->buildPolicy()); } - public function testGetPolicyCameraDomainValidMultiple() { + public function testGetPolicyCameraDomainValidMultiple(): void { $expectedPolicy = "autoplay 'none';camera www.nextcloud.com www.nextcloud.org;fullscreen 'none';geolocation 'none';microphone 'none';payment 'none'"; $this->policy->addAllowedCameraDomain('www.nextcloud.com'); @@ -72,14 +54,14 @@ class EmptyFeaturePolicyTest extends \Test\TestCase { $this->assertSame($expectedPolicy, $this->policy->buildPolicy()); } - public function testGetPolicyFullScreenDomainValid() { + public function testGetPolicyFullScreenDomainValid(): void { $expectedPolicy = "autoplay 'none';camera 'none';fullscreen www.nextcloud.com;geolocation 'none';microphone 'none';payment 'none'"; $this->policy->addAllowedFullScreenDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->policy->buildPolicy()); } - public function testGetPolicyFullScreenDomainValidMultiple() { + public function testGetPolicyFullScreenDomainValidMultiple(): void { $expectedPolicy = "autoplay 'none';camera 'none';fullscreen www.nextcloud.com www.nextcloud.org;geolocation 'none';microphone 'none';payment 'none'"; $this->policy->addAllowedFullScreenDomain('www.nextcloud.com'); @@ -87,14 +69,14 @@ class EmptyFeaturePolicyTest extends \Test\TestCase { $this->assertSame($expectedPolicy, $this->policy->buildPolicy()); } - public function testGetPolicyGeoLocationDomainValid() { + public function testGetPolicyGeoLocationDomainValid(): void { $expectedPolicy = "autoplay 'none';camera 'none';fullscreen 'none';geolocation www.nextcloud.com;microphone 'none';payment 'none'"; $this->policy->addAllowedGeoLocationDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->policy->buildPolicy()); } - public function testGetPolicyGeoLocationDomainValidMultiple() { + public function testGetPolicyGeoLocationDomainValidMultiple(): void { $expectedPolicy = "autoplay 'none';camera 'none';fullscreen 'none';geolocation www.nextcloud.com www.nextcloud.org;microphone 'none';payment 'none'"; $this->policy->addAllowedGeoLocationDomain('www.nextcloud.com'); @@ -102,14 +84,14 @@ class EmptyFeaturePolicyTest extends \Test\TestCase { $this->assertSame($expectedPolicy, $this->policy->buildPolicy()); } - public function testGetPolicyMicrophoneDomainValid() { + public function testGetPolicyMicrophoneDomainValid(): void { $expectedPolicy = "autoplay 'none';camera 'none';fullscreen 'none';geolocation 'none';microphone www.nextcloud.com;payment 'none'"; $this->policy->addAllowedMicrophoneDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->policy->buildPolicy()); } - public function testGetPolicyMicrophoneDomainValidMultiple() { + public function testGetPolicyMicrophoneDomainValidMultiple(): void { $expectedPolicy = "autoplay 'none';camera 'none';fullscreen 'none';geolocation 'none';microphone www.nextcloud.com www.nextcloud.org;payment 'none'"; $this->policy->addAllowedMicrophoneDomain('www.nextcloud.com'); @@ -117,14 +99,14 @@ class EmptyFeaturePolicyTest extends \Test\TestCase { $this->assertSame($expectedPolicy, $this->policy->buildPolicy()); } - public function testGetPolicyPaymentDomainValid() { + public function testGetPolicyPaymentDomainValid(): void { $expectedPolicy = "autoplay 'none';camera 'none';fullscreen 'none';geolocation 'none';microphone 'none';payment www.nextcloud.com"; $this->policy->addAllowedPaymentDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->policy->buildPolicy()); } - public function testGetPolicyPaymentDomainValidMultiple() { + public function testGetPolicyPaymentDomainValidMultiple(): void { $expectedPolicy = "autoplay 'none';camera 'none';fullscreen 'none';geolocation 'none';microphone 'none';payment www.nextcloud.com www.nextcloud.org"; $this->policy->addAllowedPaymentDomain('www.nextcloud.com'); diff --git a/tests/lib/AppFramework/Http/FeaturePolicyTest.php b/tests/lib/AppFramework/Http/FeaturePolicyTest.php index 869650f42b1..6ea990fb111 100644 --- a/tests/lib/AppFramework/Http/FeaturePolicyTest.php +++ b/tests/lib/AppFramework/Http/FeaturePolicyTest.php @@ -2,25 +2,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\AppFramework\Http; @@ -28,7 +11,6 @@ namespace Test\AppFramework\Http; use OCP\AppFramework\Http\FeaturePolicy; class FeaturePolicyTest extends \Test\TestCase { - /** @var EmptyFeaturePolicy */ private $policy; @@ -37,19 +19,19 @@ class FeaturePolicyTest extends \Test\TestCase { $this->policy = new FeaturePolicy(); } - public function testGetPolicyDefault() { + public function testGetPolicyDefault(): void { $defaultPolicy = "autoplay 'self';camera 'none';fullscreen 'self';geolocation 'none';microphone 'none';payment 'none'"; $this->assertSame($defaultPolicy, $this->policy->buildPolicy()); } - public function testGetPolicyAutoplayDomainValid() { + public function testGetPolicyAutoplayDomainValid(): void { $expectedPolicy = "autoplay 'self' www.nextcloud.com;camera 'none';fullscreen 'self';geolocation 'none';microphone 'none';payment 'none'"; $this->policy->addAllowedAutoplayDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->policy->buildPolicy()); } - public function testGetPolicyAutoplayDomainValidMultiple() { + public function testGetPolicyAutoplayDomainValidMultiple(): void { $expectedPolicy = "autoplay 'self' www.nextcloud.com www.nextcloud.org;camera 'none';fullscreen 'self';geolocation 'none';microphone 'none';payment 'none'"; $this->policy->addAllowedAutoplayDomain('www.nextcloud.com'); @@ -57,14 +39,14 @@ class FeaturePolicyTest extends \Test\TestCase { $this->assertSame($expectedPolicy, $this->policy->buildPolicy()); } - public function testGetPolicyCameraDomainValid() { + public function testGetPolicyCameraDomainValid(): void { $expectedPolicy = "autoplay 'self';camera www.nextcloud.com;fullscreen 'self';geolocation 'none';microphone 'none';payment 'none'"; $this->policy->addAllowedCameraDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->policy->buildPolicy()); } - public function testGetPolicyCameraDomainValidMultiple() { + public function testGetPolicyCameraDomainValidMultiple(): void { $expectedPolicy = "autoplay 'self';camera www.nextcloud.com www.nextcloud.org;fullscreen 'self';geolocation 'none';microphone 'none';payment 'none'"; $this->policy->addAllowedCameraDomain('www.nextcloud.com'); @@ -72,14 +54,14 @@ class FeaturePolicyTest extends \Test\TestCase { $this->assertSame($expectedPolicy, $this->policy->buildPolicy()); } - public function testGetPolicyFullScreenDomainValid() { + public function testGetPolicyFullScreenDomainValid(): void { $expectedPolicy = "autoplay 'self';camera 'none';fullscreen 'self' www.nextcloud.com;geolocation 'none';microphone 'none';payment 'none'"; $this->policy->addAllowedFullScreenDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->policy->buildPolicy()); } - public function testGetPolicyFullScreenDomainValidMultiple() { + public function testGetPolicyFullScreenDomainValidMultiple(): void { $expectedPolicy = "autoplay 'self';camera 'none';fullscreen 'self' www.nextcloud.com www.nextcloud.org;geolocation 'none';microphone 'none';payment 'none'"; $this->policy->addAllowedFullScreenDomain('www.nextcloud.com'); @@ -87,14 +69,14 @@ class FeaturePolicyTest extends \Test\TestCase { $this->assertSame($expectedPolicy, $this->policy->buildPolicy()); } - public function testGetPolicyGeoLocationDomainValid() { + public function testGetPolicyGeoLocationDomainValid(): void { $expectedPolicy = "autoplay 'self';camera 'none';fullscreen 'self';geolocation www.nextcloud.com;microphone 'none';payment 'none'"; $this->policy->addAllowedGeoLocationDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->policy->buildPolicy()); } - public function testGetPolicyGeoLocationDomainValidMultiple() { + public function testGetPolicyGeoLocationDomainValidMultiple(): void { $expectedPolicy = "autoplay 'self';camera 'none';fullscreen 'self';geolocation www.nextcloud.com www.nextcloud.org;microphone 'none';payment 'none'"; $this->policy->addAllowedGeoLocationDomain('www.nextcloud.com'); @@ -102,14 +84,14 @@ class FeaturePolicyTest extends \Test\TestCase { $this->assertSame($expectedPolicy, $this->policy->buildPolicy()); } - public function testGetPolicyMicrophoneDomainValid() { + public function testGetPolicyMicrophoneDomainValid(): void { $expectedPolicy = "autoplay 'self';camera 'none';fullscreen 'self';geolocation 'none';microphone www.nextcloud.com;payment 'none'"; $this->policy->addAllowedMicrophoneDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->policy->buildPolicy()); } - public function testGetPolicyMicrophoneDomainValidMultiple() { + public function testGetPolicyMicrophoneDomainValidMultiple(): void { $expectedPolicy = "autoplay 'self';camera 'none';fullscreen 'self';geolocation 'none';microphone www.nextcloud.com www.nextcloud.org;payment 'none'"; $this->policy->addAllowedMicrophoneDomain('www.nextcloud.com'); @@ -117,14 +99,14 @@ class FeaturePolicyTest extends \Test\TestCase { $this->assertSame($expectedPolicy, $this->policy->buildPolicy()); } - public function testGetPolicyPaymentDomainValid() { + public function testGetPolicyPaymentDomainValid(): void { $expectedPolicy = "autoplay 'self';camera 'none';fullscreen 'self';geolocation 'none';microphone 'none';payment www.nextcloud.com"; $this->policy->addAllowedPaymentDomain('www.nextcloud.com'); $this->assertSame($expectedPolicy, $this->policy->buildPolicy()); } - public function testGetPolicyPaymentDomainValidMultiple() { + public function testGetPolicyPaymentDomainValidMultiple(): void { $expectedPolicy = "autoplay 'self';camera 'none';fullscreen 'self';geolocation 'none';microphone 'none';payment www.nextcloud.com www.nextcloud.org"; $this->policy->addAllowedPaymentDomain('www.nextcloud.com'); diff --git a/tests/lib/AppFramework/Http/FileDisplayResponseTest.php b/tests/lib/AppFramework/Http/FileDisplayResponseTest.php index 0a244a85754..029ddaad712 100644 --- a/tests/lib/AppFramework/Http/FileDisplayResponseTest.php +++ b/tests/lib/AppFramework/Http/FileDisplayResponseTest.php @@ -1,24 +1,8 @@ <?php + /** - * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\AppFramework\Http; @@ -51,23 +35,23 @@ class FileDisplayResponseTest extends \Test\TestCase { $this->response = new FileDisplayResponse($this->file); } - public function testHeader() { + public function testHeader(): void { $headers = $this->response->getHeaders(); $this->assertArrayHasKey('Content-Disposition', $headers); $this->assertSame('inline; filename="myFileName"', $headers['Content-Disposition']); } - public function testETag() { + public function testETag(): void { $this->assertSame('myETag', $this->response->getETag()); } - public function testLastModified() { + public function testLastModified(): void { $lastModified = $this->response->getLastModified(); $this->assertNotNull($lastModified); $this->assertSame(1464825600, $lastModified->getTimestamp()); } - public function test304() { + public function test304(): void { $output = $this->getMockBuilder('OCP\AppFramework\Http\IOutput') ->disableOriginalConstructor() ->getMock(); @@ -84,7 +68,7 @@ class FileDisplayResponseTest extends \Test\TestCase { } - public function testNon304() { + public function testNon304(): void { $output = $this->getMockBuilder('OCP\AppFramework\Http\IOutput') ->disableOriginalConstructor() ->getMock(); diff --git a/tests/lib/AppFramework/Http/HttpTest.php b/tests/lib/AppFramework/Http/HttpTest.php index d3d23425f7c..d3ec8438554 100644 --- a/tests/lib/AppFramework/Http/HttpTest.php +++ b/tests/lib/AppFramework/Http/HttpTest.php @@ -1,24 +1,9 @@ <?php /** - * ownCloud - App Framework - * - * @author Bernhard Posselt - * @copyright 2012 Bernhard Posselt <dev@bernhard-posselt.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\AppFramework\Http; @@ -41,19 +26,19 @@ class HttpTest extends \Test\TestCase { } - public function testProtocol() { + public function testProtocol(): void { $header = $this->http->getStatusHeader(Http::STATUS_TEMPORARY_REDIRECT); $this->assertEquals('HTTP/1.1 307 Temporary Redirect', $header); } - public function testProtocol10() { + public function testProtocol10(): void { $this->http = new Http($this->server, 'HTTP/1.0'); $header = $this->http->getStatusHeader(Http::STATUS_OK); $this->assertEquals('HTTP/1.0 200 OK', $header); } - public function testTempRedirectBecomesFoundInHttp10() { + public function testTempRedirectBecomesFoundInHttp10(): void { $http = new Http([], 'HTTP/1.0'); $header = $http->getStatusHeader(Http::STATUS_TEMPORARY_REDIRECT); diff --git a/tests/lib/AppFramework/Http/JSONResponseTest.php b/tests/lib/AppFramework/Http/JSONResponseTest.php index 504876b2d88..56f67b23f0d 100644 --- a/tests/lib/AppFramework/Http/JSONResponseTest.php +++ b/tests/lib/AppFramework/Http/JSONResponseTest.php @@ -1,26 +1,9 @@ <?php /** - * ownCloud - App Framework - * - * @author Bernhard Posselt - * @author Morris Jobke - * @copyright 2012 Bernhard Posselt <dev@bernhard-posselt.com> - * @copyright 2013 Morris Jobke <morris.jobke@gmail.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\AppFramework\Http; @@ -29,7 +12,6 @@ use OCP\AppFramework\Http; use OCP\AppFramework\Http\JSONResponse; class JSONResponseTest extends \Test\TestCase { - /** * @var JSONResponse */ @@ -41,13 +23,13 @@ class JSONResponseTest extends \Test\TestCase { } - public function testHeader() { + public function testHeader(): void { $headers = $this->json->getHeaders(); $this->assertEquals('application/json; charset=utf-8', $headers['Content-Type']); } - public function testSetData() { + public function testSetData(): void { $params = ['hi', 'yo']; $this->json->setData($params); @@ -55,7 +37,7 @@ class JSONResponseTest extends \Test\TestCase { } - public function testSetRender() { + public function testSetRender(): void { $params = ['test' => 'hi']; $this->json->setData($params); @@ -64,10 +46,7 @@ class JSONResponseTest extends \Test\TestCase { $this->assertEquals($expected, $this->json->render()); } - /** - * @return array - */ - public function renderDataProvider() { + public static function renderDataProvider(): array { return [ [ ['test' => 'hi'], '{"test":"hi"}', @@ -79,26 +58,26 @@ class JSONResponseTest extends \Test\TestCase { } /** - * @dataProvider renderDataProvider * @param array $input * @param string $expected */ - public function testRender(array $input, $expected) { + #[\PHPUnit\Framework\Attributes\DataProvider('renderDataProvider')] + public function testRender(array $input, $expected): void { $this->json->setData($input); $this->assertEquals($expected, $this->json->render()); } - - public function testRenderWithNonUtf8Encoding() { - $this->expectException(\Exception::class); - $this->expectExceptionMessage('Could not json_encode due to invalid non UTF-8 characters in the array: array ('); + + public function testRenderWithNonUtf8Encoding(): void { + $this->expectException(\JsonException::class); + $this->expectExceptionMessage('Malformed UTF-8 characters, possibly incorrectly encoded'); $params = ['test' => hex2bin('e9')]; $this->json->setData($params); $this->json->render(); } - public function testConstructorAllowsToSetData() { + public function testConstructorAllowsToSetData(): void { $data = ['hi']; $code = 300; $response = new JSONResponse($data, $code); @@ -108,7 +87,7 @@ class JSONResponseTest extends \Test\TestCase { $this->assertEquals($code, $response->getStatus()); } - public function testChainability() { + public function testChainability(): void { $params = ['hi', 'yo']; $this->json->setData($params) ->setStatus(Http::STATUS_NOT_FOUND); diff --git a/tests/lib/AppFramework/Http/OutputTest.php b/tests/lib/AppFramework/Http/OutputTest.php index 5fe35d24bde..2ba93833dd1 100644 --- a/tests/lib/AppFramework/Http/OutputTest.php +++ b/tests/lib/AppFramework/Http/OutputTest.php @@ -1,9 +1,8 @@ <?php + /** - * Copyright (c) 2016 Robin Appelman <robin@icewind.nl> - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\AppFramework\Http; @@ -11,19 +10,19 @@ namespace Test\AppFramework\Http; use OC\AppFramework\Http\Output; class OutputTest extends \Test\TestCase { - public function testSetOutput() { + public function testSetOutput(): void { $this->expectOutputString('foo'); $output = new Output(''); $output->setOutput('foo'); } - public function testSetReadfile() { + public function testSetReadfile(): void { $this->expectOutputString(file_get_contents(__FILE__)); $output = new Output(''); $output->setReadfile(__FILE__); } - public function testSetReadfileStream() { + public function testSetReadfileStream(): void { $this->expectOutputString(file_get_contents(__FILE__)); $output = new Output(''); $output->setReadfile(fopen(__FILE__, 'r')); diff --git a/tests/lib/AppFramework/Http/PublicTemplateResponseTest.php b/tests/lib/AppFramework/Http/PublicTemplateResponseTest.php index cbf8f8303c3..cb7bd97f5da 100644 --- a/tests/lib/AppFramework/Http/PublicTemplateResponseTest.php +++ b/tests/lib/AppFramework/Http/PublicTemplateResponseTest.php @@ -1,41 +1,23 @@ <?php /** - * @copyright Copyright (c) 2018 Julius Härtl <jus@bitgrid.net> - * - * @author Julius Härtl <jus@bitgrid.net> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\AppFramework\Http; -use OCP\AppFramework\Http; use OCP\AppFramework\Http\Template\PublicTemplateResponse; +use OCP\AppFramework\Http\Template\SimpleMenuAction; use Test\TestCase; class PublicTemplateResponseTest extends TestCase { - public function testSetParamsConstructor() { + public function testSetParamsConstructor(): void { $template = new PublicTemplateResponse('app', 'home', ['key' => 'value']); - $this->assertContains('core/js/public/publicpage', \OC_Util::$scripts); $this->assertEquals(['key' => 'value'], $template->getParams()); } - public function testAdditionalElements() { + public function testAdditionalElements(): void { $template = new PublicTemplateResponse('app', 'home', ['key' => 'value']); $template->setHeaderTitle('Header'); $template->setHeaderDetails('Details'); @@ -44,9 +26,9 @@ class PublicTemplateResponseTest extends TestCase { $this->assertEquals('Details', $template->getHeaderDetails()); } - public function testActionSingle() { + public function testActionSingle(): void { $actions = [ - new Http\Template\SimpleMenuAction('link', 'Download', 'download', 'downloadLink', 0) + new SimpleMenuAction('link', 'Download', 'download', 'downloadLink', 0) ]; $template = new PublicTemplateResponse('app', 'home', ['key' => 'value']); $template->setHeaderActions($actions); @@ -57,11 +39,11 @@ class PublicTemplateResponseTest extends TestCase { } - public function testActionMultiple() { + public function testActionMultiple(): void { $actions = [ - new Http\Template\SimpleMenuAction('link1', 'Download1', 'download1', 'downloadLink1', 100), - new Http\Template\SimpleMenuAction('link2', 'Download2', 'download2', 'downloadLink2', 20), - new Http\Template\SimpleMenuAction('link3', 'Download3', 'download3', 'downloadLink3', 0) + new SimpleMenuAction('link1', 'Download1', 'download1', 'downloadLink1', 100), + new SimpleMenuAction('link2', 'Download2', 'download2', 'downloadLink2', 20), + new SimpleMenuAction('link3', 'Download3', 'download3', 'downloadLink3', 0) ]; $template = new PublicTemplateResponse('app', 'home', ['key' => 'value']); $template->setHeaderActions($actions); @@ -72,9 +54,8 @@ class PublicTemplateResponseTest extends TestCase { } - public function testGetRenderAs() { + public function testGetRenderAs(): void { $template = new PublicTemplateResponse('app', 'home', ['key' => 'value']); - $this->assertContains('core/js/public/publicpage', \OC_Util::$scripts); $this->assertEquals(['key' => 'value'], $template->getParams()); $this->assertEquals('public', $template->getRenderAs()); } diff --git a/tests/lib/AppFramework/Http/RedirectResponseTest.php b/tests/lib/AppFramework/Http/RedirectResponseTest.php index 5130d36937f..f6319782e79 100644 --- a/tests/lib/AppFramework/Http/RedirectResponseTest.php +++ b/tests/lib/AppFramework/Http/RedirectResponseTest.php @@ -1,24 +1,9 @@ <?php /** - * ownCloud - App Framework - * - * @author Bernhard Posselt - * @copyright 2012 Bernhard Posselt <dev@bernhard-posselt.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\AppFramework\Http; @@ -27,7 +12,6 @@ use OCP\AppFramework\Http; use OCP\AppFramework\Http\RedirectResponse; class RedirectResponseTest extends \Test\TestCase { - /** * @var RedirectResponse */ @@ -39,7 +23,7 @@ class RedirectResponseTest extends \Test\TestCase { } - public function testHeaders() { + public function testHeaders(): void { $headers = $this->response->getHeaders(); $this->assertEquals('/url', $headers['Location']); $this->assertEquals(Http::STATUS_SEE_OTHER, @@ -47,7 +31,7 @@ class RedirectResponseTest extends \Test\TestCase { } - public function testGetRedirectUrl() { + public function testGetRedirectUrl(): void { $this->assertEquals('/url', $this->response->getRedirectUrl()); } } diff --git a/tests/lib/AppFramework/Http/RequestIdTest.php b/tests/lib/AppFramework/Http/RequestIdTest.php index 9f9afed4b7f..9cfd3b1785c 100644 --- a/tests/lib/AppFramework/Http/RequestIdTest.php +++ b/tests/lib/AppFramework/Http/RequestIdTest.php @@ -2,23 +2,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2022 Joas Schilling <coding@schilljs.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\AppFramework\Http; @@ -64,11 +49,7 @@ class RequestIdTest extends \Test\TestCase { $this->secureRandom->expects($this->once()) ->method('generate') ->with('20') - ->willReturnOnConsecutiveCalls( - 'GeneratedByNextcloudItself1', - 'GeneratedByNextcloudItself2', - 'GeneratedByNextcloudItself3' - ); + ->willReturn('GeneratedByNextcloudItself1'); $this->assertSame('GeneratedByNextcloudItself1', $requestId->getId()); $this->assertSame('GeneratedByNextcloudItself1', $requestId->getId()); diff --git a/tests/lib/AppFramework/Http/RequestStream.php b/tests/lib/AppFramework/Http/RequestStream.php index 3868ed16505..7340391b2d5 100644 --- a/tests/lib/AppFramework/Http/RequestStream.php +++ b/tests/lib/AppFramework/Http/RequestStream.php @@ -1,5 +1,9 @@ <?php +/** + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ namespace Test\AppFramework\Http; /** @@ -7,24 +11,26 @@ namespace Test\AppFramework\Http; * Used to simulate php://input for Request tests */ class RequestStream { - protected $position; - protected $varname; + protected int $position = 0; + protected string $varname = ''; + /* @var resource */ + public $context; - public function stream_open($path, $mode, $options, &$opened_path) { + public function stream_open(string $path, string $mode, int $options, ?string &$opened_path): bool { $url = parse_url($path); - $this->varname = $url["host"]; + $this->varname = $url['host'] ?? ''; $this->position = 0; return true; } - public function stream_read($count) { + public function stream_read(int $count): string { $ret = substr($GLOBALS[$this->varname], $this->position, $count); $this->position += strlen($ret); return $ret; } - public function stream_write($data) { + public function stream_write(string $data): int { $left = substr($GLOBALS[$this->varname], 0, $this->position); $right = substr($GLOBALS[$this->varname], $this->position + strlen($data)); $GLOBALS[$this->varname] = $left . $data . $right; @@ -32,15 +38,15 @@ class RequestStream { return strlen($data); } - public function stream_tell() { + public function stream_tell(): int { return $this->position; } - public function stream_eof() { + public function stream_eof(): bool { return $this->position >= strlen($GLOBALS[$this->varname]); } - public function stream_seek($offset, $whence) { + public function stream_seek(int $offset, int $whence = SEEK_SET): bool { switch ($whence) { case SEEK_SET: if ($offset < strlen($GLOBALS[$this->varname]) && $offset >= 0) { @@ -74,7 +80,7 @@ class RequestStream { } } - public function stream_stat() { + public function stream_stat(): array { $size = strlen($GLOBALS[$this->varname]); $time = time(); $data = [ @@ -96,10 +102,10 @@ class RequestStream { //return false; } - public function stream_metadata($path, $option, $var) { + public function stream_metadata(string $path, int $option, $var): bool { if ($option == STREAM_META_TOUCH) { $url = parse_url($path); - $varname = $url["host"]; + $varname = $url['host'] ?? ''; if (!isset($GLOBALS[$varname])) { $GLOBALS[$varname] = ''; } diff --git a/tests/lib/AppFramework/Http/RequestTest.php b/tests/lib/AppFramework/Http/RequestTest.php index e15f3fe656c..7ea2cb31482 100644 --- a/tests/lib/AppFramework/Http/RequestTest.php +++ b/tests/lib/AppFramework/Http/RequestTest.php @@ -1,13 +1,10 @@ <?php + /** - * @copyright 2013 Thomas Tanghus (thomas@tanghus.net) - * @copyright 2016 Lukas Reschke lukas@owncloud.com - * - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace Test\AppFramework\Http; use OC\AppFramework\Http\Request; @@ -51,7 +48,7 @@ class RequestTest extends \Test\TestCase { parent::tearDown(); } - public function testRequestAccessors() { + public function testRequestAccessors(): void { $vars = [ 'get' => ['name' => 'John Q. Public', 'nickname' => 'Joey'], 'method' => 'GET', @@ -81,7 +78,7 @@ class RequestTest extends \Test\TestCase { } // urlParams has precedence over POST which has precedence over GET - public function testPrecedence() { + public function testPrecedence(): void { $vars = [ 'get' => ['name' => 'John Q. Public', 'nickname' => 'Joey'], 'post' => ['name' => 'Jane Doe', 'nickname' => 'Janey'], @@ -104,7 +101,7 @@ class RequestTest extends \Test\TestCase { - public function testImmutableArrayAccess() { + public function testImmutableArrayAccess(): void { $this->expectException(\RuntimeException::class); $vars = [ @@ -124,7 +121,7 @@ class RequestTest extends \Test\TestCase { } - public function testImmutableMagicAccess() { + public function testImmutableMagicAccess(): void { $this->expectException(\RuntimeException::class); $vars = [ @@ -144,7 +141,7 @@ class RequestTest extends \Test\TestCase { } - public function testGetTheMethodRight() { + public function testGetTheMethodRight(): void { $this->expectException(\LogicException::class); $vars = [ @@ -163,7 +160,7 @@ class RequestTest extends \Test\TestCase { $request->post; } - public function testTheMethodIsRight() { + public function testTheMethodIsRight(): void { $vars = [ 'get' => ['name' => 'John Q. Public', 'nickname' => 'Joey'], 'method' => 'GET', @@ -183,7 +180,7 @@ class RequestTest extends \Test\TestCase { $this->assertSame('Joey', $result['nickname']); } - public function testJsonPost() { + public function testJsonPost(): void { global $data; $data = '{"name": "John Q. Public", "nickname": "Joey"}'; $vars = [ @@ -207,9 +204,66 @@ class RequestTest extends \Test\TestCase { $this->assertSame('Joey', $request['nickname']); } - public function testNotJsonPost() { + public function testScimJsonPost(): void { global $data; - $data = 'this is not valid json'; + $data = '{"userName":"testusername", "displayName":"Example User"}'; + $vars = [ + 'method' => 'POST', + 'server' => ['CONTENT_TYPE' => 'application/scim+json; utf-8'] + ]; + + $request = new Request( + $vars, + $this->requestId, + $this->config, + $this->csrfTokenManager, + $this->stream + ); + + $this->assertSame('POST', $request->method); + $result = $request->post; + $this->assertSame('testusername', $result['userName']); + $this->assertSame('Example User', $result['displayName']); + $this->assertSame('Example User', $request->params['displayName']); + $this->assertSame('Example User', $request['displayName']); + } + + public function testCustomJsonPost(): void { + global $data; + $data = '{"propertyA":"sometestvalue", "propertyB":"someothertestvalue"}'; + + // Note: the content type used here is fictional and intended to check if the regex for JSON content types works fine + $vars = [ + 'method' => 'POST', + 'server' => ['CONTENT_TYPE' => 'application/custom-type+json; utf-8'] + ]; + + $request = new Request( + $vars, + $this->requestId, + $this->config, + $this->csrfTokenManager, + $this->stream + ); + + $this->assertSame('POST', $request->method); + $result = $request->post; + $this->assertSame('sometestvalue', $result['propertyA']); + $this->assertSame('someothertestvalue', $result['propertyB']); + } + + public static function dataNotJsonData(): array { + return [ + ['this is not valid json'], + ['"just a string"'], + ['{"just a string"}'], + ]; + } + + #[\PHPUnit\Framework\Attributes\DataProvider('dataNotJsonData')] + public function testNotJsonPost(string $testData): void { + global $data; + $data = $testData; $vars = [ 'method' => 'POST', 'server' => ['CONTENT_TYPE' => 'application/json; utf-8'] @@ -228,7 +282,49 @@ class RequestTest extends \Test\TestCase { // ensure there's no error attempting to decode the content } - public function testPatch() { + public function testNotScimJsonPost(): void { + global $data; + $data = 'this is not valid scim json'; + $vars = [ + 'method' => 'POST', + 'server' => ['CONTENT_TYPE' => 'application/scim+json; utf-8'] + ]; + + $request = new Request( + $vars, + $this->requestId, + $this->config, + $this->csrfTokenManager, + $this->stream + ); + + $this->assertEquals('POST', $request->method); + $result = $request->post; + // ensure there's no error attempting to decode the content + } + + public function testNotCustomJsonPost(): void { + global $data; + $data = 'this is not valid json'; + $vars = [ + 'method' => 'POST', + 'server' => ['CONTENT_TYPE' => 'application/custom-type+json; utf-8'] + ]; + + $request = new Request( + $vars, + $this->requestId, + $this->config, + $this->csrfTokenManager, + $this->stream + ); + + $this->assertEquals('POST', $request->method); + $result = $request->post; + // ensure there's no error attempting to decode the content + } + + public function testPatch(): void { global $data; $data = http_build_query(['name' => 'John Q. Public', 'nickname' => 'Joey'], '', '&'); @@ -252,7 +348,7 @@ class RequestTest extends \Test\TestCase { $this->assertSame('Joey', $result['nickname']); } - public function testJsonPatchAndPut() { + public function testJsonPatchAndPut(): void { global $data; // PUT content @@ -298,7 +394,99 @@ class RequestTest extends \Test\TestCase { $this->assertSame(null, $result['nickname']); } - public function testPutStream() { + public function testScimJsonPatchAndPut(): void { + global $data; + + // PUT content + $data = '{"userName": "sometestusername", "displayName": "Example User"}'; + $vars = [ + 'method' => 'PUT', + 'server' => ['CONTENT_TYPE' => 'application/scim+json; utf-8'], + ]; + + $request = new Request( + $vars, + $this->requestId, + $this->config, + $this->csrfTokenManager, + $this->stream + ); + + $this->assertSame('PUT', $request->method); + $result = $request->put; + + $this->assertSame('sometestusername', $result['userName']); + $this->assertSame('Example User', $result['displayName']); + + // PATCH content + $data = '{"userName": "sometestusername", "displayName": null}'; + $vars = [ + 'method' => 'PATCH', + 'server' => ['CONTENT_TYPE' => 'application/scim+json; utf-8'], + ]; + + $request = new Request( + $vars, + $this->requestId, + $this->config, + $this->csrfTokenManager, + $this->stream + ); + + $this->assertSame('PATCH', $request->method); + $result = $request->patch; + + $this->assertSame('sometestusername', $result['userName']); + $this->assertSame(null, $result['displayName']); + } + + public function testCustomJsonPatchAndPut(): void { + global $data; + + // PUT content + $data = '{"propertyA": "sometestvalue", "propertyB": "someothertestvalue"}'; + $vars = [ + 'method' => 'PUT', + 'server' => ['CONTENT_TYPE' => 'application/custom-type+json; utf-8'], + ]; + + $request = new Request( + $vars, + $this->requestId, + $this->config, + $this->csrfTokenManager, + $this->stream + ); + + $this->assertSame('PUT', $request->method); + $result = $request->put; + + $this->assertSame('sometestvalue', $result['propertyA']); + $this->assertSame('someothertestvalue', $result['propertyB']); + + // PATCH content + $data = '{"propertyA": "sometestvalue", "propertyB": null}'; + $vars = [ + 'method' => 'PATCH', + 'server' => ['CONTENT_TYPE' => 'application/custom-type+json; utf-8'], + ]; + + $request = new Request( + $vars, + $this->requestId, + $this->config, + $this->csrfTokenManager, + $this->stream + ); + + $this->assertSame('PATCH', $request->method); + $result = $request->patch; + + $this->assertSame('sometestvalue', $result['propertyA']); + $this->assertSame(null, $result['propertyB']); + } + + public function testPutStream(): void { global $data; $data = file_get_contents(__DIR__ . '/../../../data/testimage.png'); @@ -333,7 +521,7 @@ class RequestTest extends \Test\TestCase { } - public function testSetUrlParameters() { + public function testSetUrlParameters(): void { $vars = [ 'post' => [], 'method' => 'POST', @@ -355,254 +543,182 @@ class RequestTest extends \Test\TestCase { $this->assertEquals('3', $request->getParams()['id']); } - public function testGetRemoteAddressWithoutTrustedRemote() { - $this->config - ->expects($this->once()) - ->method('getSystemValue') - ->with('trusted_proxies') - ->willReturn([]); - - $request = new Request( - [ - 'server' => [ + public static function dataGetRemoteAddress(): array { + return [ + 'IPv4 without trusted remote' => [ + [ 'REMOTE_ADDR' => '10.0.0.2', 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4', - 'HTTP_X_FORWARDED_FOR' => '192.168.0.233' + 'HTTP_X_FORWARDED_FOR' => '192.168.0.233', ], + [], + [], + '10.0.0.2', ], - $this->requestId, - $this->config, - $this->csrfTokenManager, - $this->stream - ); - - $this->assertSame('10.0.0.2', $request->getRemoteAddress()); - } - - public function testGetRemoteAddressWithNoTrustedHeader() { - $this->config - ->expects($this->at(0)) - ->method('getSystemValue') - ->with('trusted_proxies') - ->willReturn(['10.0.0.2']); - $this->config - ->expects($this->at(1)) - ->method('getSystemValue') - ->with('forwarded_for_headers') - ->willReturn([]); - - $request = new Request( - [ - 'server' => [ + 'IPv4 without trusted headers' => [ + [ 'REMOTE_ADDR' => '10.0.0.2', 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4', - 'HTTP_X_FORWARDED_FOR' => '192.168.0.233' + 'HTTP_X_FORWARDED_FOR' => '192.168.0.233', ], + ['10.0.0.2'], + [], + '10.0.0.2', ], - $this->requestId, - $this->config, - $this->csrfTokenManager, - $this->stream - ); - - $this->assertSame('10.0.0.2', $request->getRemoteAddress()); - } - - public function testGetRemoteAddressWithSingleTrustedRemote() { - $this->config - ->expects($this->at(0)) - ->method('getSystemValue') - ->with('trusted_proxies') - ->willReturn(['10.0.0.2']); - $this->config - ->expects($this->at(1)) - ->method('getSystemValue') - ->with('forwarded_for_headers') - ->willReturn(['HTTP_X_FORWARDED']); - - $request = new Request( - [ - 'server' => [ + 'IPv4 with single trusted remote' => [ + [ 'REMOTE_ADDR' => '10.0.0.2', 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4', - 'HTTP_X_FORWARDED_FOR' => '192.168.0.233' + 'HTTP_X_FORWARDED_FOR' => '192.168.0.233', ], + ['10.0.0.2'], + ['HTTP_X_FORWARDED'], + '10.4.0.4', ], - $this->requestId, - $this->config, - $this->csrfTokenManager, - $this->stream - ); - - $this->assertSame('10.4.0.5', $request->getRemoteAddress()); - } - - public function testGetRemoteAddressIPv6WithSingleTrustedRemote() { - $this->config - ->expects($this->at(0)) - ->method('getSystemValue') - ->with('trusted_proxies') - ->willReturn(['2001:db8:85a3:8d3:1319:8a2e:370:7348']); - $this->config - ->expects($this->at(1)) - ->method('getSystemValue') - ->with('forwarded_for_headers') - ->willReturn(['HTTP_X_FORWARDED']); - - $request = new Request( - [ - 'server' => [ + 'IPv6 with single trusted remote' => [ + [ 'REMOTE_ADDR' => '2001:db8:85a3:8d3:1319:8a2e:370:7348', 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4', - 'HTTP_X_FORWARDED_FOR' => '192.168.0.233' + 'HTTP_X_FORWARDED_FOR' => '192.168.0.233', ], + ['2001:db8:85a3:8d3:1319:8a2e:370:7348'], + ['HTTP_X_FORWARDED'], + '10.4.0.4', ], - $this->requestId, - $this->config, - $this->csrfTokenManager, - $this->stream - ); - - $this->assertSame('10.4.0.5', $request->getRemoteAddress()); - } - - public function testGetRemoteAddressVerifyPriorityHeader() { - $this->config - ->expects($this->at(0)) - ->method('getSystemValue') - ->with('trusted_proxies') - ->willReturn(['10.0.0.2']); - $this->config - ->expects($this->at(1)) - ->method('getSystemValue') - ->with('forwarded_for_headers') - ->willReturn([ - 'HTTP_CLIENT_IP', - 'HTTP_X_FORWARDED_FOR', - 'HTTP_X_FORWARDED' - ]); - - $request = new Request( - [ - 'server' => [ + 'IPv4 with multiple trusted remotes' => [ + [ + 'REMOTE_ADDR' => '10.0.0.2', + 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4, ::1', + 'HTTP_X_FORWARDED_FOR' => '192.168.0.233', + ], + ['10.0.0.2', '::1'], + ['HTTP_X_FORWARDED'], + '10.4.0.4', + ], + 'IPv4 order of forwarded-for headers' => [ + [ 'REMOTE_ADDR' => '10.0.0.2', 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4', - 'HTTP_X_FORWARDED_FOR' => '192.168.0.233' + 'HTTP_X_FORWARDED_FOR' => '192.168.0.233', + ], + ['10.0.0.2'], + [ + 'HTTP_X_FORWARDED', + 'HTTP_X_FORWARDED_FOR', + 'HTTP_CLIENT_IP', ], + '192.168.0.233', ], - $this->requestId, - $this->config, - $this->csrfTokenManager, - $this->stream - ); - - $this->assertSame('192.168.0.233', $request->getRemoteAddress()); - } - - public function testGetRemoteAddressIPv6VerifyPriorityHeader() { - $this->config - ->expects($this->at(0)) - ->method('getSystemValue') - ->with('trusted_proxies') - ->willReturn(['2001:db8:85a3:8d3:1319:8a2e:370:7348']); - $this->config - ->expects($this->at(1)) - ->method('getSystemValue') - ->with('forwarded_for_headers') - ->willReturn([ - 'HTTP_CLIENT_IP', - 'HTTP_X_FORWARDED_FOR', - 'HTTP_X_FORWARDED' - ]); - - $request = new Request( - [ - 'server' => [ - 'REMOTE_ADDR' => '2001:db8:85a3:8d3:1319:8a2e:370:7348', + 'IPv4 order of forwarded-for headers (reversed)' => [ + [ + 'REMOTE_ADDR' => '10.0.0.2', 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4', - 'HTTP_X_FORWARDED_FOR' => '192.168.0.233' + 'HTTP_X_FORWARDED_FOR' => '192.168.0.233', ], + ['10.0.0.2'], + [ + 'HTTP_CLIENT_IP', + 'HTTP_X_FORWARDED_FOR', + 'HTTP_X_FORWARDED', + ], + '10.4.0.4', ], - $this->requestId, - $this->config, - $this->csrfTokenManager, - $this->stream - ); - - $this->assertSame('192.168.0.233', $request->getRemoteAddress()); - } - - public function testGetRemoteAddressWithMatchingCidrTrustedRemote() { - $this->config - ->expects($this->at(0)) - ->method('getSystemValue') - ->with('trusted_proxies') - ->willReturn(['192.168.2.0/24']); - $this->config - ->expects($this->at(1)) - ->method('getSystemValue') - ->with('forwarded_for_headers') - ->willReturn(['HTTP_X_FORWARDED_FOR']); - - $request = new Request( - [ - 'server' => [ - 'REMOTE_ADDR' => '192.168.2.99', + 'IPv6 order of forwarded-for headers' => [ + [ + 'REMOTE_ADDR' => '2001:db8:85a3:8d3:1319:8a2e:370:7348', 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4', - 'HTTP_X_FORWARDED_FOR' => '192.168.0.233' + 'HTTP_X_FORWARDED_FOR' => '192.168.0.233', + ], + ['2001:db8:85a3:8d3:1319:8a2e:370:7348'], + [ + 'HTTP_X_FORWARDED', + 'HTTP_X_FORWARDED_FOR', + 'HTTP_CLIENT_IP', ], + '192.168.0.233', ], - $this->requestId, - $this->config, - $this->csrfTokenManager, - $this->stream - ); - - $this->assertSame('192.168.0.233', $request->getRemoteAddress()); - } - - public function testGetRemoteAddressWithNotMatchingCidrTrustedRemote() { - $this->config - ->expects($this->once()) - ->method('getSystemValue') - ->with('trusted_proxies') - ->willReturn(['192.168.2.0/24']); - - $request = new Request( - [ - 'server' => [ + 'IPv4 matching CIDR of trusted proxy' => [ + [ 'REMOTE_ADDR' => '192.168.3.99', 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4', - 'HTTP_X_FORWARDED_FOR' => '192.168.0.233' + 'HTTP_X_FORWARDED_FOR' => '192.168.0.233', ], + ['192.168.2.0/24'], + ['HTTP_X_FORWARDED_FOR'], + '192.168.3.99', ], - $this->requestId, - $this->config, - $this->csrfTokenManager, - $this->stream - ); - - $this->assertSame('192.168.3.99', $request->getRemoteAddress()); + 'IPv6 matching CIDR of trusted proxy' => [ + [ + 'REMOTE_ADDR' => '2001:db8:85a3:8d3:1319:8a21:370:7348', + 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4', + 'HTTP_X_FORWARDED_FOR' => '192.168.0.233', + ], + ['2001:db8:85a3:8d3:1319:8a20::/95'], + ['HTTP_X_FORWARDED_FOR'], + '192.168.0.233', + ], + 'IPv6 not matching CIDR of trusted proxy' => [ + [ + 'REMOTE_ADDR' => '2001:db8:85a3:8d3:1319:8a2e:370:7348', + 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4', + 'HTTP_X_FORWARDED_FOR' => '192.168.0.233', + ], + ['fd::/8'], + [], + '2001:db8:85a3:8d3:1319:8a2e:370:7348', + ], + 'IPv6 with invalid trusted proxy' => [ + [ + 'REMOTE_ADDR' => '2001:db8:85a3:8d3:1319:8a2e:370:7348', + 'HTTP_X_FORWARDED' => '10.4.0.5, 10.4.0.4', + 'HTTP_X_FORWARDED_FOR' => '192.168.0.233', + ], + ['fx::/8'], + [], + '2001:db8:85a3:8d3:1319:8a2e:370:7348', + ], + 'IPv4 forwarded for IPv6' => [ + [ + 'REMOTE_ADDR' => '192.168.2.99', + 'HTTP_X_FORWARDED_FOR' => '[2001:db8:85a3:8d3:1319:8a2e:370:7348]', + ], + ['192.168.2.0/24'], + ['HTTP_X_FORWARDED_FOR'], + '2001:db8:85a3:8d3:1319:8a2e:370:7348', + ], + 'IPv4 with port' => [ + [ + 'REMOTE_ADDR' => '2001:db8:85a3:8d3:1319:8a2e:370:7348', + 'HTTP_X_FORWARDED_FOR' => '192.168.2.99:8080', + ], + ['2001:db8::/8'], + ['HTTP_X_FORWARDED_FOR'], + '192.168.2.99', + ], + 'IPv6 with port' => [ + [ + 'REMOTE_ADDR' => '192.168.2.99', + 'HTTP_X_FORWARDED_FOR' => '[2001:db8:85a3:8d3:1319:8a2e:370:7348]:8080', + ], + ['192.168.2.0/24'], + ['HTTP_X_FORWARDED_FOR'], + '2001:db8:85a3:8d3:1319:8a2e:370:7348', + ], + ]; } - public function testGetRemoteAddressWithXForwardedForIPv6() { - $this->config - ->expects($this->at(0)) - ->method('getSystemValue') - ->with('trusted_proxies') - ->willReturn(['192.168.2.0/24']); + #[\PHPUnit\Framework\Attributes\DataProvider('dataGetRemoteAddress')] + public function testGetRemoteAddress(array $headers, array $trustedProxies, array $forwardedForHeaders, string $expected): void { $this->config - ->expects($this->at(1)) ->method('getSystemValue') - ->with('forwarded_for_headers') - ->willReturn(['HTTP_X_FORWARDED_FOR']); + ->willReturnMap([ + ['trusted_proxies', [], $trustedProxies], + ['forwarded_for_headers', ['HTTP_X_FORWARDED_FOR'], $forwardedForHeaders], + ]); $request = new Request( [ - 'server' => [ - 'REMOTE_ADDR' => '192.168.2.99', - 'HTTP_X_FORWARDED_FOR' => '[2001:db8:85a3:8d3:1319:8a2e:370:7348]', - ], + 'server' => $headers, ], $this->requestId, $this->config, @@ -610,13 +726,10 @@ class RequestTest extends \Test\TestCase { $this->stream ); - $this->assertSame('2001:db8:85a3:8d3:1319:8a2e:370:7348', $request->getRemoteAddress()); + $this->assertSame($expected, $request->getRemoteAddress()); } - /** - * @return array - */ - public function httpProtocolProvider() { + public static function dataHttpProtocol(): array { return [ // Valid HTTP 1.0 ['HTTP/1.0', 'HTTP/1.0'], @@ -643,12 +756,12 @@ class RequestTest extends \Test\TestCase { } /** - * @dataProvider httpProtocolProvider * * @param mixed $input * @param string $expected */ - public function testGetHttpProtocol($input, $expected) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataHttpProtocol')] + public function testGetHttpProtocol($input, $expected): void { $request = new Request( [ 'server' => [ @@ -664,22 +777,34 @@ class RequestTest extends \Test\TestCase { $this->assertSame($expected, $request->getHttpProtocol()); } - public function testGetServerProtocolWithOverride() { + public function testGetServerProtocolWithOverrideValid(): void { $this->config - ->expects($this->at(0)) - ->method('getSystemValue') - ->with('overwriteprotocol') - ->willReturn('customProtocol'); - $this->config - ->expects($this->at(1)) - ->method('getSystemValue') - ->with('overwritecondaddr') - ->willReturn(''); + ->expects($this->exactly(3)) + ->method('getSystemValueString') + ->willReturnMap([ + ['overwriteprotocol', '', 'HTTPS'], // should be automatically lowercased + ['overwritecondaddr', '', ''], + ]); + + $request = new Request( + [], + $this->requestId, + $this->config, + $this->csrfTokenManager, + $this->stream + ); + + $this->assertSame('https', $request->getServerProtocol()); + } + + public function testGetServerProtocolWithOverrideInValid(): void { $this->config - ->expects($this->at(2)) - ->method('getSystemValue') - ->with('overwriteprotocol') - ->willReturn('customProtocol'); + ->expects($this->exactly(3)) + ->method('getSystemValueString') + ->willReturnMap([ + ['overwriteprotocol', '', 'bogusProtocol'], // should trigger fallback to http + ['overwritecondaddr', '', ''], + ]); $request = new Request( [], @@ -689,10 +814,10 @@ class RequestTest extends \Test\TestCase { $this->stream ); - $this->assertSame('customProtocol', $request->getServerProtocol()); + $this->assertSame('http', $request->getServerProtocol()); } - public function testGetServerProtocolWithProtoValid() { + public function testGetServerProtocolWithProtoValid(): void { $this->config ->method('getSystemValue') ->willReturnCallback(function ($key, $default) { @@ -733,7 +858,7 @@ class RequestTest extends \Test\TestCase { $this->assertSame('http', $requestHttp->getServerProtocol()); } - public function testGetServerProtocolWithHttpsServerValueOn() { + public function testGetServerProtocolWithHttpsServerValueOn(): void { $this->config ->method('getSystemValue') ->willReturnCallback(function ($key, $default) { @@ -754,7 +879,7 @@ class RequestTest extends \Test\TestCase { $this->assertSame('https', $request->getServerProtocol()); } - public function testGetServerProtocolWithHttpsServerValueOff() { + public function testGetServerProtocolWithHttpsServerValueOff(): void { $this->config ->method('getSystemValue') ->willReturnCallback(function ($key, $default) { @@ -775,7 +900,7 @@ class RequestTest extends \Test\TestCase { $this->assertSame('http', $request->getServerProtocol()); } - public function testGetServerProtocolWithHttpsServerValueEmpty() { + public function testGetServerProtocolWithHttpsServerValueEmpty(): void { $this->config ->method('getSystemValue') ->willReturnCallback(function ($key, $default) { @@ -796,7 +921,7 @@ class RequestTest extends \Test\TestCase { $this->assertSame('http', $request->getServerProtocol()); } - public function testGetServerProtocolDefault() { + public function testGetServerProtocolDefault(): void { $this->config ->method('getSystemValue') ->willReturnCallback(function ($key, $default) { @@ -813,7 +938,7 @@ class RequestTest extends \Test\TestCase { $this->assertSame('http', $request->getServerProtocol()); } - public function testGetServerProtocolBehindLoadBalancers() { + public function testGetServerProtocolBehindLoadBalancers(): void { $this->config ->method('getSystemValue') ->willReturnCallback(function ($key, $default) { @@ -841,12 +966,12 @@ class RequestTest extends \Test\TestCase { } /** - * @dataProvider userAgentProvider * @param string $testAgent * @param array $userAgent * @param bool $matches */ - public function testUserAgent($testAgent, $userAgent, $matches) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataUserAgent')] + public function testUserAgent($testAgent, $userAgent, $matches): void { $request = new Request( [ 'server' => [ @@ -863,12 +988,12 @@ class RequestTest extends \Test\TestCase { } /** - * @dataProvider userAgentProvider * @param string $testAgent * @param array $userAgent * @param bool $matches */ - public function testUndefinedUserAgent($testAgent, $userAgent, $matches) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataUserAgent')] + public function testUndefinedUserAgent($testAgent, $userAgent, $matches): void { $request = new Request( [], $this->requestId, @@ -880,10 +1005,7 @@ class RequestTest extends \Test\TestCase { $this->assertFalse($request->isUserAgent($userAgent)); } - /** - * @return array - */ - public function userAgentProvider() { + public static function dataUserAgent(): array { return [ [ 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)', @@ -1002,7 +1124,64 @@ class RequestTest extends \Test\TestCase { ]; } - public function testInsecureServerHostServerNameHeader() { + public static function dataMatchClientVersion(): array { + return [ + [ + 'Mozilla/5.0 (Android) Nextcloud-android/3.24.1', + Request::USER_AGENT_CLIENT_ANDROID, + '3.24.1', + ], + [ + 'Mozilla/5.0 (iOS) Nextcloud-iOS/4.8.2', + Request::USER_AGENT_CLIENT_IOS, + '4.8.2', + ], + [ + 'Mozilla/5.0 (Windows) mirall/3.8.1', + Request::USER_AGENT_CLIENT_DESKTOP, + '3.8.1', + ], + [ + 'Mozilla/5.0 (Android) Nextcloud-Talk v17.10.0', + Request::USER_AGENT_TALK_ANDROID, + '17.10.0', + ], + [ + 'Mozilla/5.0 (iOS) Nextcloud-Talk v17.0.1', + Request::USER_AGENT_TALK_IOS, + '17.0.1', + ], + [ + 'Mozilla/5.0 (Windows) Nextcloud-Talk v0.6.0', + Request::USER_AGENT_TALK_DESKTOP, + '0.6.0', + ], + [ + 'Mozilla/5.0 (Windows) Nextcloud-Outlook v1.0.0', + Request::USER_AGENT_OUTLOOK_ADDON, + '1.0.0', + ], + [ + 'Filelink for *cloud/1.0.0', + Request::USER_AGENT_THUNDERBIRD_ADDON, + '1.0.0', + ], + ]; + } + + /** + * @param string $testAgent + * @param string $userAgent + * @param string $version + */ + #[\PHPUnit\Framework\Attributes\DataProvider('dataMatchClientVersion')] + public function testMatchClientVersion(string $testAgent, string $userAgent, string $version): void { + preg_match($userAgent, $testAgent, $matches); + + $this->assertSame($version, $matches[1]); + } + + public function testInsecureServerHostServerNameHeader(): void { $request = new Request( [ 'server' => [ @@ -1018,7 +1197,7 @@ class RequestTest extends \Test\TestCase { $this->assertSame('from.server.name:8080', $request->getInsecureServerHost()); } - public function testInsecureServerHostHttpHostHeader() { + public function testInsecureServerHostHttpHostHeader(): void { $request = new Request( [ 'server' => [ @@ -1035,7 +1214,7 @@ class RequestTest extends \Test\TestCase { $this->assertSame('from.host.header:8080', $request->getInsecureServerHost()); } - public function testInsecureServerHostHttpFromForwardedHeaderSingle() { + public function testInsecureServerHostHttpFromForwardedHeaderSingle(): void { $this->config ->method('getSystemValue') ->willReturnCallback(function ($key, $default) { @@ -1064,7 +1243,7 @@ class RequestTest extends \Test\TestCase { $this->assertSame('from.forwarded.host:8080', $request->getInsecureServerHost()); } - public function testInsecureServerHostHttpFromForwardedHeaderStacked() { + public function testInsecureServerHostHttpFromForwardedHeaderStacked(): void { $this->config ->method('getSystemValue') ->willReturnCallback(function ($key, $default) { @@ -1093,9 +1272,9 @@ class RequestTest extends \Test\TestCase { $this->assertSame('from.forwarded.host2:8080', $request->getInsecureServerHost()); } - public function testGetServerHostWithOverwriteHost() { + public function testGetServerHostWithOverwriteHost(): void { $this->config - ->method('getSystemValue') + ->method('getSystemValueString') ->willReturnCallback(function ($key, $default) { if ($key === 'overwritecondaddr') { return ''; @@ -1117,7 +1296,7 @@ class RequestTest extends \Test\TestCase { $this->assertSame('my.overwritten.host', $request->getServerHost()); } - public function testGetServerHostWithTrustedDomain() { + public function testGetServerHostWithTrustedDomain(): void { $this->config ->method('getSystemValue') ->willReturnCallback(function ($key, $default) { @@ -1146,7 +1325,7 @@ class RequestTest extends \Test\TestCase { $this->assertSame('my.trusted.host', $request->getServerHost()); } - public function testGetServerHostWithUntrustedDomain() { + public function testGetServerHostWithUntrustedDomain(): void { $this->config ->method('getSystemValue') ->willReturnCallback(function ($key, $default) { @@ -1175,7 +1354,7 @@ class RequestTest extends \Test\TestCase { $this->assertSame('my.trusted.host', $request->getServerHost()); } - public function testGetServerHostWithNoTrustedDomain() { + public function testGetServerHostWithNoTrustedDomain(): void { $this->config ->method('getSystemValue') ->willReturnCallback(function ($key, $default) { @@ -1201,10 +1380,7 @@ class RequestTest extends \Test\TestCase { $this->assertSame('', $request->getServerHost()); } - /** - * @return array - */ - public function dataGetServerHostTrustedDomain() { + public static function dataGetServerHostTrustedDomain(): array { return [ 'is array' => ['my.trusted.host', ['my.trusted.host']], 'is array but undefined index 0' => ['my.trusted.host', [2 => 'my.trusted.host']], @@ -1213,12 +1389,8 @@ class RequestTest extends \Test\TestCase { ]; } - /** - * @dataProvider dataGetServerHostTrustedDomain - * @param $expected - * @param $trustedDomain - */ - public function testGetServerHostTrustedDomain($expected, $trustedDomain) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataGetServerHostTrustedDomain')] + public function testGetServerHostTrustedDomain(string $expected, $trustedDomain): void { $this->config ->method('getSystemValue') ->willReturnCallback(function ($key, $default) use ($trustedDomain) { @@ -1247,10 +1419,10 @@ class RequestTest extends \Test\TestCase { $this->assertSame($expected, $request->getServerHost()); } - public function testGetOverwriteHostDefaultNull() { + public function testGetOverwriteHostDefaultNull(): void { $this->config ->expects($this->once()) - ->method('getSystemValue') + ->method('getSystemValueString') ->with('overwritehost') ->willReturn(''); $request = new Request( @@ -1264,22 +1436,14 @@ class RequestTest extends \Test\TestCase { $this->assertNull(self::invokePrivate($request, 'getOverwriteHost')); } - public function testGetOverwriteHostWithOverwrite() { - $this->config - ->expects($this->at(0)) - ->method('getSystemValue') - ->with('overwritehost') - ->willReturn('www.owncloud.org'); - $this->config - ->expects($this->at(1)) - ->method('getSystemValue') - ->with('overwritecondaddr') - ->willReturn(''); + public function testGetOverwriteHostWithOverwrite(): void { $this->config - ->expects($this->at(2)) - ->method('getSystemValue') - ->with('overwritehost') - ->willReturn('www.owncloud.org'); + ->expects($this->exactly(3)) + ->method('getSystemValueString') + ->willReturnMap([ + ['overwritehost', '', 'www.owncloud.org'], + ['overwritecondaddr', '', ''], + ]); $request = new Request( [], @@ -1293,7 +1457,7 @@ class RequestTest extends \Test\TestCase { } - public function testGetPathInfoNotProcessible() { + public function testGetPathInfoNotProcessible(): void { $this->expectException(\Exception::class); $this->expectExceptionMessage('The requested uri(/foo.php) cannot be processed by the script \'/var/www/index.php\')'); @@ -1314,7 +1478,7 @@ class RequestTest extends \Test\TestCase { } - public function testGetRawPathInfoNotProcessible() { + public function testGetRawPathInfoNotProcessible(): void { $this->expectException(\Exception::class); $this->expectExceptionMessage('The requested uri(/foo.php) cannot be processed by the script \'/var/www/index.php\')'); @@ -1335,12 +1499,12 @@ class RequestTest extends \Test\TestCase { } /** - * @dataProvider genericPathInfoProvider * @param string $requestUri * @param string $scriptName * @param string $expected */ - public function testGetPathInfoWithoutSetEnvGeneric($requestUri, $scriptName, $expected) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataGenericPathInfo')] + public function testGetPathInfoWithoutSetEnvGeneric($requestUri, $scriptName, $expected): void { $request = new Request( [ 'server' => [ @@ -1358,12 +1522,12 @@ class RequestTest extends \Test\TestCase { } /** - * @dataProvider genericPathInfoProvider * @param string $requestUri * @param string $scriptName * @param string $expected */ - public function testGetRawPathInfoWithoutSetEnvGeneric($requestUri, $scriptName, $expected) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataGenericPathInfo')] + public function testGetRawPathInfoWithoutSetEnvGeneric($requestUri, $scriptName, $expected): void { $request = new Request( [ 'server' => [ @@ -1381,12 +1545,12 @@ class RequestTest extends \Test\TestCase { } /** - * @dataProvider rawPathInfoProvider * @param string $requestUri * @param string $scriptName * @param string $expected */ - public function testGetRawPathInfoWithoutSetEnv($requestUri, $scriptName, $expected) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataRawPathInfo')] + public function testGetRawPathInfoWithoutSetEnv($requestUri, $scriptName, $expected): void { $request = new Request( [ 'server' => [ @@ -1404,12 +1568,12 @@ class RequestTest extends \Test\TestCase { } /** - * @dataProvider pathInfoProvider * @param string $requestUri * @param string $scriptName * @param string $expected */ - public function testGetPathInfoWithoutSetEnv($requestUri, $scriptName, $expected) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataPathInfo')] + public function testGetPathInfoWithoutSetEnv($requestUri, $scriptName, $expected): void { $request = new Request( [ 'server' => [ @@ -1426,10 +1590,7 @@ class RequestTest extends \Test\TestCase { $this->assertSame($expected, $request->getPathInfo()); } - /** - * @return array - */ - public function genericPathInfoProvider() { + public static function dataGenericPathInfo(): array { return [ ['/core/index.php?XDEBUG_SESSION_START=14600', '/core/index.php', ''], ['/index.php/apps/files/', 'index.php', '/apps/files/'], @@ -1441,28 +1602,22 @@ class RequestTest extends \Test\TestCase { ]; } - /** - * @return array - */ - public function rawPathInfoProvider() { + public static function dataRawPathInfo(): array { return [ ['/foo%2Fbar/subfolder', '', 'foo%2Fbar/subfolder'], ]; } - /** - * @return array - */ - public function pathInfoProvider() { + public static function dataPathInfo(): array { return [ ['/foo%2Fbar/subfolder', '', 'foo/bar/subfolder'], ]; } - public function testGetRequestUriWithoutOverwrite() { + public function testGetRequestUriWithoutOverwrite(): void { $this->config ->expects($this->once()) - ->method('getSystemValue') + ->method('getSystemValueString') ->with('overwritewebroot') ->willReturn(''); @@ -1481,35 +1636,31 @@ class RequestTest extends \Test\TestCase { $this->assertSame('/test.php', $request->getRequestUri()); } - public function providesGetRequestUriWithOverwriteData() { + public static function dataGetRequestUriWithOverwrite(): array { return [ ['/scriptname.php/some/PathInfo', '/owncloud/', ''], - ['/scriptname.php/some/PathInfo', '/owncloud/', '123'], + ['/scriptname.php/some/PathInfo', '/owncloud/', '123', '123.123.123.123'], ]; } - /** - * @dataProvider providesGetRequestUriWithOverwriteData - */ - public function testGetRequestUriWithOverwrite($expectedUri, $overwriteWebRoot, $overwriteCondAddr) { - $this->config - ->expects($this->at(0)) - ->method('getSystemValue') - ->with('overwritewebroot') - ->willReturn($overwriteWebRoot); + #[\PHPUnit\Framework\Attributes\DataProvider('dataGetRequestUriWithOverwrite')] + public function testGetRequestUriWithOverwrite($expectedUri, $overwriteWebRoot, $overwriteCondAddr, $remoteAddr = ''): void { $this->config - ->expects($this->at(1)) - ->method('getSystemValue') - ->with('overwritecondaddr') - ->willReturn($overwriteCondAddr); + ->expects($this->exactly(2)) + ->method('getSystemValueString') + ->willReturnMap([ + ['overwritewebroot', '', $overwriteWebRoot], + ['overwritecondaddr', '', $overwriteCondAddr], + ]); - $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') - ->setMethods(['getScriptName']) + $request = $this->getMockBuilder(Request::class) + ->onlyMethods(['getScriptName']) ->setConstructorArgs([ [ 'server' => [ 'REQUEST_URI' => '/test.php/some/PathInfo', 'SCRIPT_NAME' => '/test.php', + 'REMOTE_ADDR' => $remoteAddr ] ], $this->requestId, @@ -1526,10 +1677,10 @@ class RequestTest extends \Test\TestCase { $this->assertSame($expectedUri, $request->getRequestUri()); } - public function testPassesCSRFCheckWithGet() { + public function testPassesCSRFCheckWithGet(): void { /** @var Request $request */ - $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') - ->setMethods(['getScriptName']) + $request = $this->getMockBuilder(Request::class) + ->onlyMethods(['getScriptName']) ->setConstructorArgs([ [ 'get' => [ @@ -1556,10 +1707,10 @@ class RequestTest extends \Test\TestCase { $this->assertTrue($request->passesCSRFCheck()); } - public function testPassesCSRFCheckWithPost() { + public function testPassesCSRFCheckWithPost(): void { /** @var Request $request */ - $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') - ->setMethods(['getScriptName']) + $request = $this->getMockBuilder(Request::class) + ->onlyMethods(['getScriptName']) ->setConstructorArgs([ [ 'post' => [ @@ -1586,10 +1737,10 @@ class RequestTest extends \Test\TestCase { $this->assertTrue($request->passesCSRFCheck()); } - public function testPassesCSRFCheckWithHeader() { + public function testPassesCSRFCheckWithHeader(): void { /** @var Request $request */ - $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') - ->setMethods(['getScriptName']) + $request = $this->getMockBuilder(Request::class) + ->onlyMethods(['getScriptName']) ->setConstructorArgs([ [ 'server' => [ @@ -1616,10 +1767,10 @@ class RequestTest extends \Test\TestCase { $this->assertTrue($request->passesCSRFCheck()); } - public function testPassesCSRFCheckWithGetAndWithoutCookies() { + public function testPassesCSRFCheckWithGetAndWithoutCookies(): void { /** @var Request $request */ - $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') - ->setMethods(['getScriptName']) + $request = $this->getMockBuilder(Request::class) + ->onlyMethods(['getScriptName']) ->setConstructorArgs([ [ 'get' => [ @@ -1640,10 +1791,10 @@ class RequestTest extends \Test\TestCase { $this->assertTrue($request->passesCSRFCheck()); } - public function testPassesCSRFCheckWithPostAndWithoutCookies() { + public function testPassesCSRFCheckWithPostAndWithoutCookies(): void { /** @var Request $request */ - $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') - ->setMethods(['getScriptName']) + $request = $this->getMockBuilder(Request::class) + ->onlyMethods(['getScriptName']) ->setConstructorArgs([ [ 'post' => [ @@ -1664,10 +1815,10 @@ class RequestTest extends \Test\TestCase { $this->assertTrue($request->passesCSRFCheck()); } - public function testPassesCSRFCheckWithHeaderAndWithoutCookies() { + public function testPassesCSRFCheckWithHeaderAndWithoutCookies(): void { /** @var Request $request */ - $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') - ->setMethods(['getScriptName']) + $request = $this->getMockBuilder(Request::class) + ->onlyMethods(['getScriptName']) ->setConstructorArgs([ [ 'server' => [ @@ -1688,10 +1839,10 @@ class RequestTest extends \Test\TestCase { $this->assertTrue($request->passesCSRFCheck()); } - public function testFailsCSRFCheckWithHeaderAndNotAllChecksPassing() { + public function testFailsCSRFCheckWithHeaderAndNotAllChecksPassing(): void { /** @var Request $request */ - $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') - ->setMethods(['getScriptName']) + $request = $this->getMockBuilder(Request::class) + ->onlyMethods(['getScriptName']) ->setConstructorArgs([ [ 'server' => [ @@ -1715,10 +1866,10 @@ class RequestTest extends \Test\TestCase { $this->assertFalse($request->passesCSRFCheck()); } - public function testPassesStrictCookieCheckWithAllCookiesAndStrict() { + public function testPassesStrictCookieCheckWithAllCookiesAndStrict(): void { /** @var Request $request */ - $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') - ->setMethods(['getScriptName', 'getCookieParams']) + $request = $this->getMockBuilder(Request::class) + ->onlyMethods(['getScriptName', 'getCookieParams']) ->setConstructorArgs([ [ 'server' => [ @@ -1747,10 +1898,10 @@ class RequestTest extends \Test\TestCase { $this->assertTrue($request->passesStrictCookieCheck()); } - public function testFailsStrictCookieCheckWithAllCookiesAndMissingStrict() { + public function testFailsStrictCookieCheckWithAllCookiesAndMissingStrict(): void { /** @var Request $request */ - $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') - ->setMethods(['getScriptName', 'getCookieParams']) + $request = $this->getMockBuilder(Request::class) + ->onlyMethods(['getScriptName', 'getCookieParams']) ->setConstructorArgs([ [ 'server' => [ @@ -1779,10 +1930,10 @@ class RequestTest extends \Test\TestCase { $this->assertFalse($request->passesStrictCookieCheck()); } - public function testGetCookieParams() { + public function testGetCookieParams(): void { /** @var Request $request */ $request = $this->getMockBuilder(Request::class) - ->setMethods(['getScriptName']) + ->onlyMethods(['getScriptName']) ->setConstructorArgs([ [], $this->requestId, @@ -1795,10 +1946,10 @@ class RequestTest extends \Test\TestCase { $this->assertSame(session_get_cookie_params(), $actual); } - public function testPassesStrictCookieCheckWithAllCookies() { + public function testPassesStrictCookieCheckWithAllCookies(): void { /** @var Request $request */ - $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') - ->setMethods(['getScriptName']) + $request = $this->getMockBuilder(Request::class) + ->onlyMethods(['getScriptName']) ->setConstructorArgs([ [ 'server' => [ @@ -1820,10 +1971,10 @@ class RequestTest extends \Test\TestCase { $this->assertTrue($request->passesStrictCookieCheck()); } - public function testPassesStrictCookieCheckWithRandomCookies() { + public function testPassesStrictCookieCheckWithRandomCookies(): void { /** @var Request $request */ - $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') - ->setMethods(['getScriptName']) + $request = $this->getMockBuilder(Request::class) + ->onlyMethods(['getScriptName']) ->setConstructorArgs([ [ 'server' => [ @@ -1843,10 +1994,10 @@ class RequestTest extends \Test\TestCase { $this->assertTrue($request->passesStrictCookieCheck()); } - public function testFailsStrictCookieCheckWithSessionCookie() { + public function testFailsStrictCookieCheckWithSessionCookie(): void { /** @var Request $request */ - $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') - ->setMethods(['getScriptName']) + $request = $this->getMockBuilder(Request::class) + ->onlyMethods(['getScriptName']) ->setConstructorArgs([ [ 'server' => [ @@ -1866,10 +2017,10 @@ class RequestTest extends \Test\TestCase { $this->assertFalse($request->passesStrictCookieCheck()); } - public function testFailsStrictCookieCheckWithRememberMeCookie() { + public function testFailsStrictCookieCheckWithRememberMeCookie(): void { /** @var Request $request */ - $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') - ->setMethods(['getScriptName']) + $request = $this->getMockBuilder(Request::class) + ->onlyMethods(['getScriptName']) ->setConstructorArgs([ [ 'server' => [ @@ -1889,10 +2040,10 @@ class RequestTest extends \Test\TestCase { $this->assertFalse($request->passesStrictCookieCheck()); } - public function testFailsCSRFCheckWithPostAndWithCookies() { + public function testFailsCSRFCheckWithPostAndWithCookies(): void { /** @var Request $request */ - $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') - ->setMethods(['getScriptName']) + $request = $this->getMockBuilder(Request::class) + ->onlyMethods(['getScriptName']) ->setConstructorArgs([ [ 'post' => [ @@ -1916,10 +2067,10 @@ class RequestTest extends \Test\TestCase { $this->assertFalse($request->passesCSRFCheck()); } - public function testFailStrictCookieCheckWithOnlyLaxCookie() { + public function testFailStrictCookieCheckWithOnlyLaxCookie(): void { /** @var Request $request */ - $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') - ->setMethods(['getScriptName']) + $request = $this->getMockBuilder(Request::class) + ->onlyMethods(['getScriptName']) ->setConstructorArgs([ [ 'server' => [ @@ -1940,10 +2091,10 @@ class RequestTest extends \Test\TestCase { $this->assertFalse($request->passesStrictCookieCheck()); } - public function testFailStrictCookieCheckWithOnlyStrictCookie() { + public function testFailStrictCookieCheckWithOnlyStrictCookie(): void { /** @var Request $request */ - $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') - ->setMethods(['getScriptName']) + $request = $this->getMockBuilder(Request::class) + ->onlyMethods(['getScriptName']) ->setConstructorArgs([ [ 'server' => [ @@ -1964,10 +2115,10 @@ class RequestTest extends \Test\TestCase { $this->assertFalse($request->passesStrictCookieCheck()); } - public function testPassesLaxCookieCheck() { + public function testPassesLaxCookieCheck(): void { /** @var Request $request */ - $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') - ->setMethods(['getScriptName']) + $request = $this->getMockBuilder(Request::class) + ->onlyMethods(['getScriptName']) ->setConstructorArgs([ [ 'server' => [ @@ -1988,10 +2139,10 @@ class RequestTest extends \Test\TestCase { $this->assertTrue($request->passesLaxCookieCheck()); } - public function testFailsLaxCookieCheckWithOnlyStrictCookie() { + public function testFailsLaxCookieCheckWithOnlyStrictCookie(): void { /** @var Request $request */ - $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') - ->setMethods(['getScriptName']) + $request = $this->getMockBuilder(Request::class) + ->onlyMethods(['getScriptName']) ->setConstructorArgs([ [ 'server' => [ @@ -2012,10 +2163,10 @@ class RequestTest extends \Test\TestCase { $this->assertFalse($request->passesLaxCookieCheck()); } - public function testSkipCookieCheckForOCSRequests() { + public function testSkipCookieCheckForOCSRequests(): void { /** @var Request $request */ - $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') - ->setMethods(['getScriptName']) + $request = $this->getMockBuilder(Request::class) + ->onlyMethods(['getScriptName']) ->setConstructorArgs([ [ 'server' => [ @@ -2037,10 +2188,7 @@ class RequestTest extends \Test\TestCase { $this->assertTrue($request->passesStrictCookieCheck()); } - /** - * @return array - */ - public function invalidTokenDataProvider() { + public static function dataInvalidToken(): array { return [ ['InvalidSentToken'], ['InvalidSentToken:InvalidSecret'], @@ -2048,14 +2196,11 @@ class RequestTest extends \Test\TestCase { ]; } - /** - * @dataProvider invalidTokenDataProvider - * @param string $invalidToken - */ - public function testPassesCSRFCheckWithInvalidToken($invalidToken) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataInvalidToken')] + public function testPassesCSRFCheckWithInvalidToken(string $invalidToken): void { /** @var Request $request */ - $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') - ->setMethods(['getScriptName']) + $request = $this->getMockBuilder(Request::class) + ->onlyMethods(['getScriptName']) ->setConstructorArgs([ [ 'server' => [ @@ -2079,10 +2224,10 @@ class RequestTest extends \Test\TestCase { $this->assertFalse($request->passesCSRFCheck()); } - public function testPassesCSRFCheckWithoutTokenFail() { + public function testPassesCSRFCheckWithoutTokenFail(): void { /** @var Request $request */ - $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') - ->setMethods(['getScriptName']) + $request = $this->getMockBuilder(Request::class) + ->onlyMethods(['getScriptName']) ->setConstructorArgs([ [], $this->requestId, @@ -2094,4 +2239,24 @@ class RequestTest extends \Test\TestCase { $this->assertFalse($request->passesCSRFCheck()); } + + public function testPassesCSRFCheckWithOCSAPIRequestHeader(): void { + /** @var Request $request */ + $request = $this->getMockBuilder(Request::class) + ->onlyMethods(['getScriptName']) + ->setConstructorArgs([ + [ + 'server' => [ + 'HTTP_OCS_APIREQUEST' => 'true', + ], + ], + $this->requestId, + $this->config, + $this->csrfTokenManager, + $this->stream + ]) + ->getMock(); + + $this->assertTrue($request->passesCSRFCheck()); + } } diff --git a/tests/lib/AppFramework/Http/ResponseTest.php b/tests/lib/AppFramework/Http/ResponseTest.php index c725e2fb602..4c76695f6e4 100644 --- a/tests/lib/AppFramework/Http/ResponseTest.php +++ b/tests/lib/AppFramework/Http/ResponseTest.php @@ -1,36 +1,22 @@ <?php /** - * ownCloud - App Framework - * - * @author Bernhard Posselt - * @copyright 2012 Bernhard Posselt <dev@bernhard-posselt.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\AppFramework\Http; use OCP\AppFramework\Http; +use OCP\AppFramework\Http\ContentSecurityPolicy; +use OCP\AppFramework\Http\EmptyContentSecurityPolicy; use OCP\AppFramework\Http\Response; use OCP\AppFramework\Utility\ITimeFactory; class ResponseTest extends \Test\TestCase { - /** - * @var \OCP\AppFramework\Http\Response + * @var Response */ private $childResponse; @@ -40,35 +26,37 @@ class ResponseTest extends \Test\TestCase { } - public function testAddHeader() { + public function testAddHeader(): void { $this->childResponse->addHeader(' hello ', 'world'); $headers = $this->childResponse->getHeaders(); $this->assertEquals('world', $headers['hello']); } - public function testSetHeaders() { + public function testSetHeaders(): void { $expected = [ 'Last-Modified' => 1, 'ETag' => 3, 'Something-Else' => 'hi', - 'X-Robots-Tag' => 'none', + 'X-Robots-Tag' => 'noindex, nofollow', + 'Cache-Control' => 'no-cache, no-store, must-revalidate', ]; $this->childResponse->setHeaders($expected); - $headers = $this->childResponse->getHeaders(); $expected['Content-Security-Policy'] = "default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none'"; $expected['Feature-Policy'] = "autoplay 'none';camera 'none';fullscreen 'none';geolocation 'none';microphone 'none';payment 'none'"; + $headers = $this->childResponse->getHeaders(); + unset($headers['X-Request-Id']); + $this->assertEquals($expected, $headers); } - public function testOverwriteCsp() { + public function testOverwriteCsp(): void { $expected = [ 'Content-Security-Policy' => "default-src 'none';base-uri 'none';manifest-src 'self';script-src 'self' 'unsafe-inline';style-src 'self' 'unsafe-inline';img-src 'self';font-src 'self' data:;connect-src 'self';media-src 'self'", ]; - $policy = new Http\ContentSecurityPolicy(); - $policy->allowInlineScript(true); + $policy = new ContentSecurityPolicy(); $this->childResponse->setContentSecurityPolicy($policy); $headers = $this->childResponse->getHeaders(); @@ -76,32 +64,31 @@ class ResponseTest extends \Test\TestCase { $this->assertEquals(array_merge($expected, $headers), $headers); } - public function testGetCsp() { - $policy = new Http\ContentSecurityPolicy(); - $policy->allowInlineScript(true); + public function testGetCsp(): void { + $policy = new ContentSecurityPolicy(); $this->childResponse->setContentSecurityPolicy($policy); $this->assertEquals($policy, $this->childResponse->getContentSecurityPolicy()); } - public function testGetCspEmpty() { - $this->assertEquals(new Http\EmptyContentSecurityPolicy(), $this->childResponse->getContentSecurityPolicy()); + public function testGetCspEmpty(): void { + $this->assertEquals(new EmptyContentSecurityPolicy(), $this->childResponse->getContentSecurityPolicy()); } - public function testAddHeaderValueNullDeletesIt() { + public function testAddHeaderValueNullDeletesIt(): void { $this->childResponse->addHeader('hello', 'world'); $this->childResponse->addHeader('hello', null); $this->assertEquals(5, count($this->childResponse->getHeaders())); } - public function testCacheHeadersAreDisabledByDefault() { + public function testCacheHeadersAreDisabledByDefault(): void { $headers = $this->childResponse->getHeaders(); $this->assertEquals('no-cache, no-store, must-revalidate', $headers['Cache-Control']); } - public function testAddCookie() { + public function testAddCookie(): void { $this->childResponse->addCookie('foo', 'bar'); $this->childResponse->addCookie('bar', 'foo', new \DateTime('1970-01-01')); @@ -121,7 +108,7 @@ class ResponseTest extends \Test\TestCase { } - public function testSetCookies() { + public function testSetCookies(): void { $expected = [ 'foo' => [ 'value' => 'bar', @@ -140,7 +127,7 @@ class ResponseTest extends \Test\TestCase { } - public function testInvalidateCookie() { + public function testInvalidateCookie(): void { $this->childResponse->addCookie('foo', 'bar'); $this->childResponse->invalidateCookie('foo'); $expected = [ @@ -157,7 +144,7 @@ class ResponseTest extends \Test\TestCase { } - public function testInvalidateCookies() { + public function testInvalidateCookies(): void { $this->childResponse->addCookie('foo', 'bar'); $this->childResponse->addCookie('bar', 'foo'); $expected = [ @@ -194,12 +181,12 @@ class ResponseTest extends \Test\TestCase { } - public function testRenderReturnNullByDefault() { + public function testRenderReturnNullByDefault(): void { $this->assertEquals(null, $this->childResponse->render()); } - public function testGetStatus() { + public function testGetStatus(): void { $default = $this->childResponse->getStatus(); $this->childResponse->setStatus(Http::STATUS_NOT_FOUND); @@ -209,13 +196,13 @@ class ResponseTest extends \Test\TestCase { } - public function testGetEtag() { + public function testGetEtag(): void { $this->childResponse->setEtag('hi'); $this->assertSame('hi', $this->childResponse->getEtag()); } - public function testGetLastModified() { + public function testGetLastModified(): void { $lastModified = new \DateTime('now', new \DateTimeZone('GMT')); $lastModified->setTimestamp(1); $this->childResponse->setLastModified($lastModified); @@ -224,17 +211,16 @@ class ResponseTest extends \Test\TestCase { - public function testCacheSecondsZero() { + public function testCacheSecondsZero(): void { $this->childResponse->cacheFor(0); $headers = $this->childResponse->getHeaders(); $this->assertEquals('no-cache, no-store, must-revalidate', $headers['Cache-Control']); - $this->assertFalse(isset($headers['Pragma'])); $this->assertFalse(isset($headers['Expires'])); } - public function testCacheSeconds() { + public function testCacheSeconds(): void { $time = $this->createMock(ITimeFactory::class); $time->method('getTime') ->willReturn(1234567); @@ -245,21 +231,20 @@ class ResponseTest extends \Test\TestCase { $headers = $this->childResponse->getHeaders(); $this->assertEquals('private, max-age=33, must-revalidate', $headers['Cache-Control']); - $this->assertEquals('private', $headers['Pragma']); - $this->assertEquals('Thu, 15 Jan 1970 06:56:40 +0000', $headers['Expires']); + $this->assertEquals('Thu, 15 Jan 1970 06:56:40 GMT', $headers['Expires']); } - public function testEtagLastModifiedHeaders() { + public function testEtagLastModifiedHeaders(): void { $lastModified = new \DateTime('now', new \DateTimeZone('GMT')); $lastModified->setTimestamp(1); $this->childResponse->setLastModified($lastModified); $headers = $this->childResponse->getHeaders(); - $this->assertEquals('Thu, 01 Jan 1970 00:00:01 +0000', $headers['Last-Modified']); + $this->assertEquals('Thu, 01 Jan 1970 00:00:01 GMT', $headers['Last-Modified']); } - public function testChainability() { + public function testChainability(): void { $lastModified = new \DateTime('now', new \DateTimeZone('GMT')); $lastModified->setTimestamp(1); @@ -274,18 +259,18 @@ class ResponseTest extends \Test\TestCase { $this->assertEquals('world', $headers['hello']); $this->assertEquals(Http::STATUS_NOT_FOUND, $this->childResponse->getStatus()); $this->assertEquals('hi', $this->childResponse->getEtag()); - $this->assertEquals('Thu, 01 Jan 1970 00:00:01 +0000', $headers['Last-Modified']); + $this->assertEquals('Thu, 01 Jan 1970 00:00:01 GMT', $headers['Last-Modified']); $this->assertEquals('private, max-age=33, must-revalidate', $headers['Cache-Control']); } - public function testThrottle() { + public function testThrottle(): void { $this->assertFalse($this->childResponse->isThrottled()); $this->childResponse->throttle(); $this->assertTrue($this->childResponse->isThrottled()); } - public function testGetThrottleMetadata() { + public function testGetThrottleMetadata(): void { $this->childResponse->throttle(['foo' => 'bar']); $this->assertSame(['foo' => 'bar'], $this->childResponse->getThrottleMetadata()); } diff --git a/tests/lib/AppFramework/Http/StreamResponseTest.php b/tests/lib/AppFramework/Http/StreamResponseTest.php index 97aced01506..87f6097a07a 100644 --- a/tests/lib/AppFramework/Http/StreamResponseTest.php +++ b/tests/lib/AppFramework/Http/StreamResponseTest.php @@ -1,24 +1,9 @@ <?php /** - * ownCloud - App Framework - * - * @author Bernhard Posselt - * @copyright 2015 Bernhard Posselt <dev@bernhard-posselt.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\AppFramework\Http; @@ -28,7 +13,6 @@ use OCP\AppFramework\Http\IOutput; use OCP\AppFramework\Http\StreamResponse; class StreamResponseTest extends \Test\TestCase { - /** @var IOutput */ private $output; @@ -39,7 +23,7 @@ class StreamResponseTest extends \Test\TestCase { ->getMock(); } - public function testOutputNotModified() { + public function testOutputNotModified(): void { $path = __FILE__; $this->output->expects($this->once()) ->method('getHttpResponseCode') @@ -51,7 +35,7 @@ class StreamResponseTest extends \Test\TestCase { $response->callback($this->output); } - public function testOutputOk() { + public function testOutputOk(): void { $path = __FILE__; $this->output->expects($this->once()) ->method('getHttpResponseCode') @@ -65,7 +49,7 @@ class StreamResponseTest extends \Test\TestCase { $response->callback($this->output); } - public function testOutputNotFound() { + public function testOutputNotFound(): void { $path = __FILE__ . 'test'; $this->output->expects($this->once()) ->method('getHttpResponseCode') @@ -80,7 +64,7 @@ class StreamResponseTest extends \Test\TestCase { $response->callback($this->output); } - public function testOutputReadFileError() { + public function testOutputReadFileError(): void { $path = __FILE__; $this->output->expects($this->once()) ->method('getHttpResponseCode') diff --git a/tests/lib/AppFramework/Http/TemplateResponseTest.php b/tests/lib/AppFramework/Http/TemplateResponseTest.php index 6cbf112494e..28f952e35e3 100644 --- a/tests/lib/AppFramework/Http/TemplateResponseTest.php +++ b/tests/lib/AppFramework/Http/TemplateResponseTest.php @@ -1,24 +1,9 @@ <?php /** - * ownCloud - App Framework - * - * @author Bernhard Posselt - * @copyright 2012 Bernhard Posselt <dev@bernhard-posselt.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\AppFramework\Http; @@ -27,9 +12,8 @@ use OCP\AppFramework\Http; use OCP\AppFramework\Http\TemplateResponse; class TemplateResponseTest extends \Test\TestCase { - /** - * @var \OCP\AppFramework\Http\TemplateResponse + * @var TemplateResponse */ private $tpl; @@ -40,7 +24,7 @@ class TemplateResponseTest extends \Test\TestCase { } - public function testSetParamsConstructor() { + public function testSetParamsConstructor(): void { $params = ['hi' => 'yo']; $this->tpl = new TemplateResponse('app', 'home', $params); @@ -48,7 +32,7 @@ class TemplateResponseTest extends \Test\TestCase { } - public function testSetRenderAsConstructor() { + public function testSetRenderAsConstructor(): void { $renderAs = 'myrender'; $this->tpl = new TemplateResponse('app', 'home', [], $renderAs); @@ -56,7 +40,7 @@ class TemplateResponseTest extends \Test\TestCase { } - public function testSetParams() { + public function testSetParams(): void { $params = ['hi' => 'yo']; $this->tpl->setParams($params); @@ -64,17 +48,17 @@ class TemplateResponseTest extends \Test\TestCase { } - public function testGetTemplateName() { + public function testGetTemplateName(): void { $this->assertEquals('home', $this->tpl->getTemplateName()); } - public function testGetRenderAs() { + public function testGetRenderAs(): void { $render = 'myrender'; $this->tpl->renderAs($render); $this->assertEquals($render, $this->tpl->getRenderAs()); } - public function testChainability() { + public function testChainability(): void { $params = ['hi' => 'yo']; $this->tpl->setParams($params) ->setStatus(Http::STATUS_NOT_FOUND); diff --git a/tests/lib/AppFramework/Middleware/AdditionalScriptsMiddlewareTest.php b/tests/lib/AppFramework/Middleware/AdditionalScriptsMiddlewareTest.php index 5127248215b..4fa5de62b0b 100644 --- a/tests/lib/AppFramework/Middleware/AdditionalScriptsMiddlewareTest.php +++ b/tests/lib/AppFramework/Middleware/AdditionalScriptsMiddlewareTest.php @@ -2,25 +2,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\AppFramework\Middleware; @@ -35,12 +18,8 @@ use OCP\AppFramework\PublicShareController; use OCP\EventDispatcher\IEventDispatcher; use OCP\IUserSession; use PHPUnit\Framework\MockObject\MockObject; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; class AdditionalScriptsMiddlewareTest extends \Test\TestCase { - - /** @var EventDispatcherInterface|MockObject */ - private $legacyDispatcher; /** @var IUserSession|MockObject */ private $userSession; @@ -55,11 +34,9 @@ class AdditionalScriptsMiddlewareTest extends \Test\TestCase { protected function setUp(): void { parent::setUp(); - $this->legacyDispatcher = $this->createMock(EventDispatcherInterface::class); $this->userSession = $this->createMock(IUserSession::class); $this->dispatcher = $this->createMock(IEventDispatcher::class); $this->middleWare = new AdditionalScriptsMiddleware( - $this->legacyDispatcher, $this->userSession, $this->dispatcher ); @@ -67,9 +44,7 @@ class AdditionalScriptsMiddlewareTest extends \Test\TestCase { $this->controller = $this->createMock(Controller::class); } - public function testNoTemplateResponse() { - $this->legacyDispatcher->expects($this->never()) - ->method($this->anything()); + public function testNoTemplateResponse(): void { $this->userSession->expects($this->never()) ->method($this->anything()); $this->dispatcher->expects($this->never()) @@ -78,9 +53,7 @@ class AdditionalScriptsMiddlewareTest extends \Test\TestCase { $this->middleWare->afterController($this->controller, 'myMethod', $this->createMock(Response::class)); } - public function testPublicShareController() { - $this->legacyDispatcher->expects($this->never()) - ->method($this->anything()); + public function testPublicShareController(): void { $this->userSession->expects($this->never()) ->method($this->anything()); $this->dispatcher->expects($this->never()) @@ -89,21 +62,12 @@ class AdditionalScriptsMiddlewareTest extends \Test\TestCase { $this->middleWare->afterController($this->createMock(PublicShareController::class), 'myMethod', $this->createMock(Response::class)); } - public function testStandaloneTemplateResponse() { - $this->legacyDispatcher->expects($this->once()) - ->method('dispatch') - ->willReturnCallback(function ($eventName) { - if ($eventName === TemplateResponse::EVENT_LOAD_ADDITIONAL_SCRIPTS) { - return; - } - - $this->fail('Wrong event dispatched'); - }); + public function testStandaloneTemplateResponse(): void { $this->userSession->expects($this->never()) ->method($this->anything()); $this->dispatcher->expects($this->once()) ->method('dispatchTyped') - ->willReturnCallback(function ($event) { + ->willReturnCallback(function ($event): void { if ($event instanceof BeforeTemplateRenderedEvent && $event->isLoggedIn() === false) { return; } @@ -114,21 +78,12 @@ class AdditionalScriptsMiddlewareTest extends \Test\TestCase { $this->middleWare->afterController($this->controller, 'myMethod', $this->createMock(StandaloneTemplateResponse::class)); } - public function testTemplateResponseNotLoggedIn() { - $this->legacyDispatcher->expects($this->once()) - ->method('dispatch') - ->willReturnCallback(function ($eventName) { - if ($eventName === TemplateResponse::EVENT_LOAD_ADDITIONAL_SCRIPTS) { - return; - } - - $this->fail('Wrong event dispatched'); - }); + public function testTemplateResponseNotLoggedIn(): void { $this->userSession->method('isLoggedIn') ->willReturn(false); $this->dispatcher->expects($this->once()) ->method('dispatchTyped') - ->willReturnCallback(function ($event) { + ->willReturnCallback(function ($event): void { if ($event instanceof BeforeTemplateRenderedEvent && $event->isLoggedIn() === false) { return; } @@ -139,25 +94,14 @@ class AdditionalScriptsMiddlewareTest extends \Test\TestCase { $this->middleWare->afterController($this->controller, 'myMethod', $this->createMock(TemplateResponse::class)); } - public function testTemplateResponseLoggedIn() { + public function testTemplateResponseLoggedIn(): void { $events = []; - $this->legacyDispatcher->expects($this->exactly(2)) - ->method('dispatch') - ->willReturnCallback(function ($eventName) use (&$events) { - if ($eventName === TemplateResponse::EVENT_LOAD_ADDITIONAL_SCRIPTS || - $eventName === TemplateResponse::EVENT_LOAD_ADDITIONAL_SCRIPTS_LOGGEDIN) { - $events[] = $eventName; - return; - } - - $this->fail('Wrong event dispatched'); - }); $this->userSession->method('isLoggedIn') ->willReturn(true); $this->dispatcher->expects($this->once()) ->method('dispatchTyped') - ->willReturnCallback(function ($event) { + ->willReturnCallback(function ($event): void { if ($event instanceof BeforeTemplateRenderedEvent && $event->isLoggedIn() === true) { return; } @@ -166,8 +110,5 @@ class AdditionalScriptsMiddlewareTest extends \Test\TestCase { }); $this->middleWare->afterController($this->controller, 'myMethod', $this->createMock(TemplateResponse::class)); - - $this->assertContains(TemplateResponse::EVENT_LOAD_ADDITIONAL_SCRIPTS, $events); - $this->assertContains(TemplateResponse::EVENT_LOAD_ADDITIONAL_SCRIPTS_LOGGEDIN, $events); } } diff --git a/tests/lib/AppFramework/Middleware/CompressionMiddlewareTest.php b/tests/lib/AppFramework/Middleware/CompressionMiddlewareTest.php index 5dd13942097..010ce3fff6d 100644 --- a/tests/lib/AppFramework/Middleware/CompressionMiddlewareTest.php +++ b/tests/lib/AppFramework/Middleware/CompressionMiddlewareTest.php @@ -2,25 +2,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\AppFramework\Middleware; @@ -35,7 +18,6 @@ use OCP\AppFramework\Http\JSONResponse; use OCP\IRequest; class CompressionMiddlewareTest extends \Test\TestCase { - /** @var IRequest */ private $request; /** @var Controller */ @@ -54,7 +36,7 @@ class CompressionMiddlewareTest extends \Test\TestCase { $this->controller = $this->createMock(Controller::class); } - public function testGzipOCSV1() { + public function testGzipOCSV1(): void { $this->request->method('getHeader') ->with('Accept-Encoding') ->willReturn('gzip'); @@ -76,7 +58,7 @@ class CompressionMiddlewareTest extends \Test\TestCase { $this->assertSame($output, gzdecode($result)); } - public function testGzipOCSV2() { + public function testGzipOCSV2(): void { $this->request->method('getHeader') ->with('Accept-Encoding') ->willReturn('gzip'); @@ -98,7 +80,7 @@ class CompressionMiddlewareTest extends \Test\TestCase { $this->assertSame($output, gzdecode($result)); } - public function testGzipJSONResponse() { + public function testGzipJSONResponse(): void { $this->request->method('getHeader') ->with('Accept-Encoding') ->willReturn('gzip'); @@ -120,7 +102,7 @@ class CompressionMiddlewareTest extends \Test\TestCase { $this->assertSame($output, gzdecode($result)); } - public function testNoGzipDataResponse() { + public function testNoGzipDataResponse(): void { $this->request->method('getHeader') ->with('Accept-Encoding') ->willReturn('gzip'); @@ -140,7 +122,7 @@ class CompressionMiddlewareTest extends \Test\TestCase { $this->assertSame($output, $result); } - public function testNoGzipNo200() { + public function testNoGzipNo200(): void { $this->request->method('getHeader') ->with('Accept-Encoding') ->willReturn('gzip'); diff --git a/tests/lib/AppFramework/Middleware/MiddlewareDispatcherTest.php b/tests/lib/AppFramework/Middleware/MiddlewareDispatcherTest.php index f408320971d..aae1c53456b 100644 --- a/tests/lib/AppFramework/Middleware/MiddlewareDispatcherTest.php +++ b/tests/lib/AppFramework/Middleware/MiddlewareDispatcherTest.php @@ -1,30 +1,16 @@ <?php /** - * ownCloud - App Framework - * - * @author Bernhard Posselt - * @copyright 2012 Bernhard Posselt <dev@bernhard-posselt.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\AppFramework\Middleware; use OC\AppFramework\Http\Request; use OC\AppFramework\Middleware\MiddlewareDispatcher; +use OCP\AppFramework\Controller; use OCP\AppFramework\Http\Response; use OCP\AppFramework\Middleware; use OCP\IConfig; @@ -48,17 +34,16 @@ class TestMiddleware extends Middleware { public $response; public $output; - private $beforeControllerThrowsEx; - /** * @param boolean $beforeControllerThrowsEx */ - public function __construct($beforeControllerThrowsEx) { + public function __construct( + private $beforeControllerThrowsEx, + ) { self::$beforeControllerCalled = 0; self::$afterControllerCalled = 0; self::$afterExceptionCalled = 0; self::$beforeOutputCalled = 0; - $this->beforeControllerThrowsEx = $beforeControllerThrowsEx; } public function beforeController($controller, $methodName) { @@ -99,6 +84,10 @@ class TestMiddleware extends Middleware { } } +class TestController extends Controller { + public function method(): void { + } +} class MiddlewareDispatcherTest extends \Test\TestCase { public $exception; @@ -125,8 +114,8 @@ class MiddlewareDispatcherTest extends \Test\TestCase { private function getControllerMock() { - return $this->getMockBuilder('OCP\AppFramework\Controller') - ->setMethods(['method']) + return $this->getMockBuilder(TestController::class) + ->onlyMethods(['method']) ->setConstructorArgs(['app', new Request( ['method' => 'GET'], @@ -144,20 +133,20 @@ class MiddlewareDispatcherTest extends \Test\TestCase { } - public function testAfterExceptionShouldReturnResponseOfMiddleware() { + public function testAfterExceptionShouldReturnResponseOfMiddleware(): void { $response = new Response(); - $m1 = $this->getMockBuilder('\OCP\AppFramework\Middleware') - ->setMethods(['afterException', 'beforeController']) + $m1 = $this->getMockBuilder(Middleware::class) + ->onlyMethods(['afterException', 'beforeController']) ->getMock(); $m1->expects($this->never()) - ->method('afterException'); + ->method('afterException'); - $m2 = $this->getMockBuilder('OCP\AppFramework\Middleware') - ->setMethods(['afterException', 'beforeController']) + $m2 = $this->getMockBuilder(Middleware::class) + ->onlyMethods(['afterException', 'beforeController']) ->getMock(); $m2->expects($this->once()) - ->method('afterException') - ->willReturn($response); + ->method('afterException') + ->willReturn($response); $this->dispatcher->registerMiddleware($m1); $this->dispatcher->registerMiddleware($m2); @@ -167,7 +156,7 @@ class MiddlewareDispatcherTest extends \Test\TestCase { } - public function testAfterExceptionShouldThrowAgainWhenNotHandled() { + public function testAfterExceptionShouldThrowAgainWhenNotHandled(): void { $m1 = new TestMiddleware(false); $m2 = new TestMiddleware(true); @@ -180,7 +169,7 @@ class MiddlewareDispatcherTest extends \Test\TestCase { } - public function testBeforeControllerCorrectArguments() { + public function testBeforeControllerCorrectArguments(): void { $m1 = $this->getMiddleware(); $this->dispatcher->beforeController($this->controller, $this->method); @@ -189,7 +178,7 @@ class MiddlewareDispatcherTest extends \Test\TestCase { } - public function testAfterControllerCorrectArguments() { + public function testAfterControllerCorrectArguments(): void { $m1 = $this->getMiddleware(); $this->dispatcher->afterController($this->controller, $this->method, $this->response); @@ -200,7 +189,7 @@ class MiddlewareDispatcherTest extends \Test\TestCase { } - public function testAfterExceptionCorrectArguments() { + public function testAfterExceptionCorrectArguments(): void { $m1 = $this->getMiddleware(); $this->expectException(\Exception::class); @@ -214,7 +203,7 @@ class MiddlewareDispatcherTest extends \Test\TestCase { } - public function testBeforeOutputCorrectArguments() { + public function testBeforeOutputCorrectArguments(): void { $m1 = $this->getMiddleware(); $this->dispatcher->beforeOutput($this->controller, $this->method, $this->out); @@ -225,7 +214,7 @@ class MiddlewareDispatcherTest extends \Test\TestCase { } - public function testBeforeControllerOrder() { + public function testBeforeControllerOrder(): void { $m1 = $this->getMiddleware(); $m2 = $this->getMiddleware(); @@ -235,7 +224,7 @@ class MiddlewareDispatcherTest extends \Test\TestCase { $this->assertEquals(2, $m2->beforeControllerOrder); } - public function testAfterControllerOrder() { + public function testAfterControllerOrder(): void { $m1 = $this->getMiddleware(); $m2 = $this->getMiddleware(); @@ -246,7 +235,7 @@ class MiddlewareDispatcherTest extends \Test\TestCase { } - public function testAfterExceptionOrder() { + public function testAfterExceptionOrder(): void { $m1 = $this->getMiddleware(); $m2 = $this->getMiddleware(); @@ -259,7 +248,7 @@ class MiddlewareDispatcherTest extends \Test\TestCase { } - public function testBeforeOutputOrder() { + public function testBeforeOutputOrder(): void { $m1 = $this->getMiddleware(); $m2 = $this->getMiddleware(); @@ -270,16 +259,16 @@ class MiddlewareDispatcherTest extends \Test\TestCase { } - public function testExceptionShouldRunAfterExceptionOfOnlyPreviouslyExecutedMiddlewares() { + public function testExceptionShouldRunAfterExceptionOfOnlyPreviouslyExecutedMiddlewares(): void { $m1 = $this->getMiddleware(); $m2 = $this->getMiddleware(true); $m3 = $this->createMock(Middleware::class); $m3->expects($this->never()) - ->method('afterException'); + ->method('afterException'); $m3->expects($this->never()) - ->method('beforeController'); + ->method('beforeController'); $m3->expects($this->never()) - ->method('afterController'); + ->method('afterController'); $m3->method('beforeOutput') ->willReturnArgument(2); diff --git a/tests/lib/AppFramework/Middleware/MiddlewareTest.php b/tests/lib/AppFramework/Middleware/MiddlewareTest.php index bd61aab4673..addd9683122 100644 --- a/tests/lib/AppFramework/Middleware/MiddlewareTest.php +++ b/tests/lib/AppFramework/Middleware/MiddlewareTest.php @@ -1,42 +1,26 @@ <?php /** - * ownCloud - App Framework - * - * @author Bernhard Posselt - * @copyright 2012 Bernhard Posselt <dev@bernhard-posselt.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\AppFramework\Middleware; +use OC\AppFramework\DependencyInjection\DIContainer; use OC\AppFramework\Http\Request; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\Response; use OCP\AppFramework\Middleware; use OCP\IConfig; use OCP\IRequestId; -use OC\AppFramework\DependencyInjection\DIContainer; class ChildMiddleware extends Middleware { }; class MiddlewareTest extends \Test\TestCase { - /** * @var Middleware */ @@ -52,12 +36,9 @@ class MiddlewareTest extends \Test\TestCase { $this->middleware = new ChildMiddleware(); - $this->api = $this->getMockBuilder(DIContainer::class) - ->disableOriginalConstructor() - ->getMock(); + $this->api = $this->createMock(DIContainer::class); $this->controller = $this->getMockBuilder(Controller::class) - ->setMethods([]) ->setConstructorArgs([ $this->api, new Request( @@ -67,31 +48,31 @@ class MiddlewareTest extends \Test\TestCase { ) ])->getMock(); $this->exception = new \Exception(); - $this->response = $this->getMockBuilder(Response::class)->getMock(); + $this->response = $this->createMock(Response::class); } - public function testBeforeController() { - $this->middleware->beforeController($this->controller, null); + public function testBeforeController(): void { + $this->middleware->beforeController($this->controller, ''); $this->assertNull(null); } - public function testAfterExceptionRaiseAgainWhenUnhandled() { + public function testAfterExceptionRaiseAgainWhenUnhandled(): void { $this->expectException(\Exception::class); - $this->middleware->afterException($this->controller, null, $this->exception); + $this->middleware->afterException($this->controller, '', $this->exception); } - public function testAfterControllerReturnResponseWhenUnhandled() { - $response = $this->middleware->afterController($this->controller, null, $this->response); + public function testAfterControllerReturnResponseWhenUnhandled(): void { + $response = $this->middleware->afterController($this->controller, '', $this->response); $this->assertEquals($this->response, $response); } - public function testBeforeOutputReturnOutputhenUnhandled() { - $output = $this->middleware->beforeOutput($this->controller, null, 'test'); + public function testBeforeOutputReturnOutputhenUnhandled(): void { + $output = $this->middleware->beforeOutput($this->controller, '', 'test'); $this->assertEquals('test', $output); } diff --git a/tests/lib/AppFramework/Middleware/NotModifiedMiddlewareTest.php b/tests/lib/AppFramework/Middleware/NotModifiedMiddlewareTest.php index c62384302fb..7dcb28a2af4 100644 --- a/tests/lib/AppFramework/Middleware/NotModifiedMiddlewareTest.php +++ b/tests/lib/AppFramework/Middleware/NotModifiedMiddlewareTest.php @@ -2,25 +2,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\AppFramework\Middleware; @@ -28,10 +11,10 @@ namespace Test\AppFramework\Middleware; use OC\AppFramework\Middleware\NotModifiedMiddleware; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Response; use OCP\IRequest; class NotModifiedMiddlewareTest extends \Test\TestCase { - /** @var IRequest */ private $request; /** @var Controller */ @@ -50,7 +33,7 @@ class NotModifiedMiddlewareTest extends \Test\TestCase { $this->controller = $this->createMock(Controller::class); } - public function dataModified(): array { + public static function dataModified(): array { $now = new \DateTime(); return [ @@ -61,20 +44,18 @@ class NotModifiedMiddlewareTest extends \Test\TestCase { [null, '"etag"', null, '', false], ['etag', '"etag"', null, '', true], - [null, '', $now, $now->format(\DateTimeInterface::RFC2822), true], + [null, '', $now, $now->format(\DateTimeInterface::RFC7231), true], [null, '', $now, $now->format(\DateTimeInterface::ATOM), false], - [null, '', null, $now->format(\DateTimeInterface::RFC2822), false], + [null, '', null, $now->format(\DateTimeInterface::RFC7231), false], [null, '', $now, '', false], ['etag', '"etag"', $now, $now->format(\DateTimeInterface::ATOM), true], - ['etag', '"etag"', $now, $now->format(\DateTimeInterface::RFC2822), true], + ['etag', '"etag"', $now, $now->format(\DateTimeInterface::RFC7231), true], ]; } - /** - * @dataProvider dataModified - */ - public function testMiddleware(?string $etag, string $etagHeader, ?\DateTime $lastModified, string $lastModifiedHeader, bool $notModifiedSet) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataModified')] + public function testMiddleware(?string $etag, string $etagHeader, ?\DateTime $lastModified, string $lastModifiedHeader, bool $notModifiedSet): void { $this->request->method('getHeader') ->willReturnCallback(function (string $name) use ($etagHeader, $lastModifiedHeader) { if ($name === 'IF_NONE_MATCH') { @@ -86,7 +67,7 @@ class NotModifiedMiddlewareTest extends \Test\TestCase { return ''; }); - $response = new Http\Response(); + $response = new Response(); if ($etag !== null) { $response->setETag($etag); } diff --git a/tests/lib/AppFramework/Middleware/OCSMiddlewareTest.php b/tests/lib/AppFramework/Middleware/OCSMiddlewareTest.php index 83b764f6c65..e5c6a417a4b 100644 --- a/tests/lib/AppFramework/Middleware/OCSMiddlewareTest.php +++ b/tests/lib/AppFramework/Middleware/OCSMiddlewareTest.php @@ -1,23 +1,8 @@ <?php + /** - * - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\AppFramework\Middleware; @@ -28,6 +13,8 @@ use OC\AppFramework\OCS\V1Response; use OC\AppFramework\OCS\V2Response; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; +use OCP\AppFramework\Http\JSONResponse; +use OCP\AppFramework\Http\Response; use OCP\AppFramework\OCS\OCSBadRequestException; use OCP\AppFramework\OCS\OCSException; use OCP\AppFramework\OCS\OCSForbiddenException; @@ -36,7 +23,6 @@ use OCP\AppFramework\OCSController; use OCP\IRequest; class OCSMiddlewareTest extends \Test\TestCase { - /** * @var IRequest */ @@ -49,49 +35,35 @@ class OCSMiddlewareTest extends \Test\TestCase { ->getMock(); } - public function dataAfterException() { - $OCSController = $this->getMockBuilder(OCSController::class) - ->disableOriginalConstructor() - ->getMock(); - $controller = $this->getMockBuilder(Controller::class) - ->disableOriginalConstructor() - ->getMock(); - + public static function dataAfterException(): array { return [ - [$OCSController, new \Exception(), true], - [$OCSController, new OCSException(), false, '', Http::STATUS_INTERNAL_SERVER_ERROR], - [$OCSController, new OCSException('foo'), false, 'foo', Http::STATUS_INTERNAL_SERVER_ERROR], - [$OCSController, new OCSException('foo', Http::STATUS_IM_A_TEAPOT), false, 'foo', Http::STATUS_IM_A_TEAPOT], - [$OCSController, new OCSBadRequestException(), false, '', Http::STATUS_BAD_REQUEST], - [$OCSController, new OCSBadRequestException('foo'), false, 'foo', Http::STATUS_BAD_REQUEST], - [$OCSController, new OCSForbiddenException(), false, '', Http::STATUS_FORBIDDEN], - [$OCSController, new OCSForbiddenException('foo'), false, 'foo', Http::STATUS_FORBIDDEN], - [$OCSController, new OCSNotFoundException(), false, '', Http::STATUS_NOT_FOUND], - [$OCSController, new OCSNotFoundException('foo'), false, 'foo', Http::STATUS_NOT_FOUND], - - [$controller, new \Exception(), true], - [$controller, new OCSException(), true], - [$controller, new OCSException('foo'), true], - [$controller, new OCSException('foo', Http::STATUS_IM_A_TEAPOT), true], - [$controller, new OCSBadRequestException(), true], - [$controller, new OCSBadRequestException('foo'), true], - [$controller, new OCSForbiddenException(), true], - [$controller, new OCSForbiddenException('foo'), true], - [$controller, new OCSNotFoundException(), true], - [$controller, new OCSNotFoundException('foo'), true], + [OCSController::class, new \Exception(), true], + [OCSController::class, new OCSException(), false, '', Http::STATUS_INTERNAL_SERVER_ERROR], + [OCSController::class, new OCSException('foo'), false, 'foo', Http::STATUS_INTERNAL_SERVER_ERROR], + [OCSController::class, new OCSException('foo', Http::STATUS_IM_A_TEAPOT), false, 'foo', Http::STATUS_IM_A_TEAPOT], + [OCSController::class, new OCSBadRequestException(), false, '', Http::STATUS_BAD_REQUEST], + [OCSController::class, new OCSBadRequestException('foo'), false, 'foo', Http::STATUS_BAD_REQUEST], + [OCSController::class, new OCSForbiddenException(), false, '', Http::STATUS_FORBIDDEN], + [OCSController::class, new OCSForbiddenException('foo'), false, 'foo', Http::STATUS_FORBIDDEN], + [OCSController::class, new OCSNotFoundException(), false, '', Http::STATUS_NOT_FOUND], + [OCSController::class, new OCSNotFoundException('foo'), false, 'foo', Http::STATUS_NOT_FOUND], + + [Controller::class, new \Exception(), true], + [Controller::class, new OCSException(), true], + [Controller::class, new OCSException('foo'), true], + [Controller::class, new OCSException('foo', Http::STATUS_IM_A_TEAPOT), true], + [Controller::class, new OCSBadRequestException(), true], + [Controller::class, new OCSBadRequestException('foo'), true], + [Controller::class, new OCSForbiddenException(), true], + [Controller::class, new OCSForbiddenException('foo'), true], + [Controller::class, new OCSNotFoundException(), true], + [Controller::class, new OCSNotFoundException('foo'), true], ]; } - /** - * @dataProvider dataAfterException - * - * @param Controller $controller - * @param \Exception $exception - * @param bool $forward - * @param string $message - * @param int $code - */ - public function testAfterExceptionOCSv1($controller, $exception, $forward, $message = '', $code = 0) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataAfterException')] + public function testAfterExceptionOCSv1(string $controller, \Exception $exception, bool $forward, string $message = '', int $code = 0): void { + $controller = $this->createMock($controller); $this->request ->method('getScriptName') ->willReturn('/ocs/v1.php'); @@ -110,7 +82,7 @@ class OCSMiddlewareTest extends \Test\TestCase { $this->assertSame($message, $this->invokePrivate($result, 'statusMessage')); if ($exception->getCode() === 0) { - $this->assertSame(\OCP\AppFramework\OCSController::RESPOND_UNKNOWN_ERROR, $result->getOCSStatus()); + $this->assertSame(OCSController::RESPOND_UNKNOWN_ERROR, $result->getOCSStatus()); } else { $this->assertSame($code, $result->getOCSStatus()); } @@ -118,16 +90,9 @@ class OCSMiddlewareTest extends \Test\TestCase { $this->assertSame(Http::STATUS_OK, $result->getStatus()); } - /** - * @dataProvider dataAfterException - * - * @param Controller $controller - * @param \Exception $exception - * @param bool $forward - * @param string $message - * @param int $code - */ - public function testAfterExceptionOCSv2($controller, $exception, $forward, $message = '', $code = 0) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataAfterException')] + public function testAfterExceptionOCSv2(string $controller, \Exception $exception, bool $forward, string $message = '', int $code = 0): void { + $controller = $this->createMock($controller); $this->request ->method('getScriptName') ->willReturn('/ocs/v2.php'); @@ -145,23 +110,16 @@ class OCSMiddlewareTest extends \Test\TestCase { $this->assertSame($message, $this->invokePrivate($result, 'statusMessage')); if ($exception->getCode() === 0) { - $this->assertSame(\OCP\AppFramework\OCSController::RESPOND_UNKNOWN_ERROR, $result->getOCSStatus()); + $this->assertSame(OCSController::RESPOND_UNKNOWN_ERROR, $result->getOCSStatus()); } else { $this->assertSame($code, $result->getOCSStatus()); } $this->assertSame($code, $result->getStatus()); } - /** - * @dataProvider dataAfterException - * - * @param Controller $controller - * @param \Exception $exception - * @param bool $forward - * @param string $message - * @param int $code - */ - public function testAfterExceptionOCSv2SubFolder($controller, $exception, $forward, $message = '', $code = 0) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataAfterException')] + public function testAfterExceptionOCSv2SubFolder(string $controller, \Exception $exception, bool $forward, string $message = '', int $code = 0): void { + $controller = $this->createMock($controller); $this->request ->method('getScriptName') ->willReturn('/mysubfolder/ocs/v2.php'); @@ -169,7 +127,7 @@ class OCSMiddlewareTest extends \Test\TestCase { $OCSMiddleware->beforeController($controller, 'method'); if ($forward) { - $this->expectException(get_class($exception)); + $this->expectException($exception::class); $this->expectExceptionMessage($exception->getMessage()); } @@ -179,46 +137,33 @@ class OCSMiddlewareTest extends \Test\TestCase { $this->assertSame($message, $this->invokePrivate($result, 'statusMessage')); if ($exception->getCode() === 0) { - $this->assertSame(\OCP\AppFramework\OCSController::RESPOND_UNKNOWN_ERROR, $result->getOCSStatus()); + $this->assertSame(OCSController::RESPOND_UNKNOWN_ERROR, $result->getOCSStatus()); } else { $this->assertSame($code, $result->getOCSStatus()); } $this->assertSame($code, $result->getStatus()); } - public function dataAfterController() { - $OCSController = $this->getMockBuilder(OCSController::class) - ->disableOriginalConstructor() - ->getMock(); - $controller = $this->getMockBuilder(Controller::class) - ->disableOriginalConstructor() - ->getMock(); - + public static function dataAfterController(): array { return [ - [$OCSController, new Http\Response(), false], - [$OCSController, new Http\JSONResponse(), false], - [$OCSController, new Http\JSONResponse(['message' => 'foo']), false], - [$OCSController, new Http\JSONResponse(['message' => 'foo'], Http::STATUS_UNAUTHORIZED), true, OCSController::RESPOND_UNAUTHORISED], - [$OCSController, new Http\JSONResponse(['message' => 'foo'], Http::STATUS_FORBIDDEN), true], - - [$controller, new Http\Response(), false], - [$controller, new Http\JSONResponse(), false], - [$controller, new Http\JSONResponse(['message' => 'foo']), false], - [$controller, new Http\JSONResponse(['message' => 'foo'], Http::STATUS_UNAUTHORIZED), false], - [$controller, new Http\JSONResponse(['message' => 'foo'], Http::STATUS_FORBIDDEN), false], + [OCSController::class, new Response(), false], + [OCSController::class, new JSONResponse(), false], + [OCSController::class, new JSONResponse(['message' => 'foo']), false], + [OCSController::class, new JSONResponse(['message' => 'foo'], Http::STATUS_UNAUTHORIZED), true, OCSController::RESPOND_UNAUTHORISED], + [OCSController::class, new JSONResponse(['message' => 'foo'], Http::STATUS_FORBIDDEN), true], + + [Controller::class, new Response(), false], + [Controller::class, new JSONResponse(), false], + [Controller::class, new JSONResponse(['message' => 'foo']), false], + [Controller::class, new JSONResponse(['message' => 'foo'], Http::STATUS_UNAUTHORIZED), false], + [Controller::class, new JSONResponse(['message' => 'foo'], Http::STATUS_FORBIDDEN), false], ]; } - /** - * @dataProvider dataAfterController - * - * @param Controller $controller - * @param Http\Response $response - * @param bool $converted - * @param int $convertedOCSStatus - */ - public function testAfterController($controller, $response, $converted, $convertedOCSStatus = 0) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataAfterController')] + public function testAfterController(string $controller, Response $response, bool $converted, int $convertedOCSStatus = 0): void { + $controller = $this->createMock($controller); $OCSMiddleware = new OCSMiddleware($this->request); $newResponse = $OCSMiddleware->afterController($controller, 'foo', $response); diff --git a/tests/lib/AppFramework/Middleware/PublicShare/PublicShareMiddlewareTest.php b/tests/lib/AppFramework/Middleware/PublicShare/PublicShareMiddlewareTest.php index 7e7140971e4..e87ee7fd565 100644 --- a/tests/lib/AppFramework/Middleware/PublicShare/PublicShareMiddlewareTest.php +++ b/tests/lib/AppFramework/Middleware/PublicShare/PublicShareMiddlewareTest.php @@ -1,24 +1,8 @@ <?php + /** - * @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\AppFramework\Middleware\PublicShare; @@ -27,23 +11,26 @@ use OC\AppFramework\Middleware\PublicShare\Exceptions\NeedAuthenticationExceptio use OC\AppFramework\Middleware\PublicShare\PublicShareMiddleware; use OCP\AppFramework\AuthPublicShareController; use OCP\AppFramework\Controller; -use OCP\AppFramework\Http\NotFoundResponse; +use OCP\AppFramework\Http; use OCP\AppFramework\Http\RedirectResponse; +use OCP\AppFramework\Http\TemplateResponse; use OCP\AppFramework\PublicShareController; use OCP\Files\NotFoundException; use OCP\IConfig; use OCP\IRequest; use OCP\ISession; use OCP\IURLGenerator; +use OCP\Security\Bruteforce\IThrottler; class PublicShareMiddlewareTest extends \Test\TestCase { - /** @var IRequest|\PHPUnit\Framework\MockObject\MockObject */ private $request; /** @var ISession|\PHPUnit\Framework\MockObject\MockObject */ private $session; /** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */ private $config; + /** @var IThrottler|\PHPUnit\Framework\MockObject\MockObject */ + private $throttler; /** @var PublicShareMiddleware */ private $middleware; @@ -55,22 +42,24 @@ class PublicShareMiddlewareTest extends \Test\TestCase { $this->request = $this->createMock(IRequest::class); $this->session = $this->createMock(ISession::class); $this->config = $this->createMock(IConfig::class); + $this->throttler = $this->createMock(IThrottler::class); $this->middleware = new PublicShareMiddleware( $this->request, $this->session, - $this->config + $this->config, + $this->throttler ); } - public function testBeforeControllerNoPublicShareController() { + public function testBeforeControllerNoPublicShareController(): void { $controller = $this->createMock(Controller::class); $this->middleware->beforeController($controller, 'method'); $this->assertTrue(true); } - public function dataShareApi() { + public static function dataShareApi(): array { return [ ['no', 'no',], ['no', 'yes',], @@ -78,10 +67,8 @@ class PublicShareMiddlewareTest extends \Test\TestCase { ]; } - /** - * @dataProvider dataShareApi - */ - public function testBeforeControllerShareApiDisabled(string $shareApi, string $shareLinks) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataShareApi')] + public function testBeforeControllerShareApiDisabled(string $shareApi, string $shareLinks): void { $controller = $this->createMock(PublicShareController::class); $this->config->method('getAppValue') @@ -94,7 +81,7 @@ class PublicShareMiddlewareTest extends \Test\TestCase { $this->middleware->beforeController($controller, 'mehod'); } - public function testBeforeControllerNoTokenParam() { + public function testBeforeControllerNoTokenParam(): void { $controller = $this->createMock(PublicShareController::class); $this->config->method('getAppValue') @@ -107,7 +94,7 @@ class PublicShareMiddlewareTest extends \Test\TestCase { $this->middleware->beforeController($controller, 'mehod'); } - public function testBeforeControllerInvalidToken() { + public function testBeforeControllerInvalidToken(): void { $controller = $this->createMock(PublicShareController::class); $this->config->method('getAppValue') @@ -129,7 +116,7 @@ class PublicShareMiddlewareTest extends \Test\TestCase { $this->middleware->beforeController($controller, 'mehod'); } - public function testBeforeControllerValidTokenNotAuthenticated() { + public function testBeforeControllerValidTokenNotAuthenticated(): void { $controller = $this->getMockBuilder(PublicShareController::class) ->setConstructorArgs(['app', $this->request, $this->session]) ->getMock(); @@ -154,7 +141,7 @@ class PublicShareMiddlewareTest extends \Test\TestCase { $this->middleware->beforeController($controller, 'mehod'); } - public function testBeforeControllerValidTokenAuthenticateMethod() { + public function testBeforeControllerValidTokenAuthenticateMethod(): void { $controller = $this->getMockBuilder(PublicShareController::class) ->setConstructorArgs(['app', $this->request, $this->session]) ->getMock(); @@ -179,7 +166,7 @@ class PublicShareMiddlewareTest extends \Test\TestCase { $this->assertTrue(true); } - public function testBeforeControllerValidTokenShowAuthenticateMethod() { + public function testBeforeControllerValidTokenShowAuthenticateMethod(): void { $controller = $this->getMockBuilder(PublicShareController::class) ->setConstructorArgs(['app', $this->request, $this->session]) ->getMock(); @@ -204,7 +191,7 @@ class PublicShareMiddlewareTest extends \Test\TestCase { $this->assertTrue(true); } - public function testBeforeControllerAuthPublicShareController() { + public function testBeforeControllerAuthPublicShareController(): void { $controller = $this->getMockBuilder(AuthPublicShareController::class) ->setConstructorArgs(['app', $this->request, $this->session, $this->createMock(IURLGenerator::class)]) ->getMock(); @@ -233,7 +220,7 @@ class PublicShareMiddlewareTest extends \Test\TestCase { $this->middleware->beforeController($controller, 'method'); } - public function testAfterExceptionNoPublicShareController() { + public function testAfterExceptionNoPublicShareController(): void { $controller = $this->createMock(Controller::class); $exception = new \Exception(); @@ -244,15 +231,16 @@ class PublicShareMiddlewareTest extends \Test\TestCase { } } - public function testAfterExceptionPublicShareControllerNotFoundException() { + public function testAfterExceptionPublicShareControllerNotFoundException(): void { $controller = $this->createMock(PublicShareController::class); $exception = new NotFoundException(); $result = $this->middleware->afterException($controller, 'method', $exception); - $this->assertInstanceOf(NotFoundResponse::class, $result); + $this->assertInstanceOf(TemplateResponse::class, $result); + $this->assertEquals($result->getStatus(), Http::STATUS_NOT_FOUND); } - public function testAfterExceptionPublicShareController() { + public function testAfterExceptionPublicShareController(): void { $controller = $this->createMock(PublicShareController::class); $exception = new \Exception(); @@ -263,7 +251,7 @@ class PublicShareMiddlewareTest extends \Test\TestCase { } } - public function testAfterExceptionAuthPublicShareController() { + public function testAfterExceptionAuthPublicShareController(): void { $controller = $this->getMockBuilder(AuthPublicShareController::class) ->setConstructorArgs([ 'app', diff --git a/tests/lib/AppFramework/Middleware/Security/BruteForceMiddlewareTest.php b/tests/lib/AppFramework/Middleware/Security/BruteForceMiddlewareTest.php index cc04992ae18..3fd2cb38a33 100644 --- a/tests/lib/AppFramework/Middleware/Security/BruteForceMiddlewareTest.php +++ b/tests/lib/AppFramework/Middleware/Security/BruteForceMiddlewareTest.php @@ -1,69 +1,70 @@ <?php + /** - * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\AppFramework\Middleware\Security; use OC\AppFramework\Middleware\Security\BruteForceMiddleware; use OC\AppFramework\Utility\ControllerMethodReflector; -use OC\Security\Bruteforce\Throttler; use OCP\AppFramework\Controller; +use OCP\AppFramework\Http\Attribute\BruteForceProtection; use OCP\AppFramework\Http\Response; use OCP\IRequest; +use OCP\Security\Bruteforce\IThrottler; +use Psr\Log\LoggerInterface; use Test\TestCase; +class TestController extends Controller { + /** + * @BruteForceProtection(action=login) + */ + public function testMethodWithAnnotation() { + } + + public function testMethodWithoutAnnotation() { + } + + #[BruteForceProtection(action: 'single')] + public function singleAttribute(): void { + } + + #[BruteForceProtection(action: 'first')] + #[BruteForceProtection(action: 'second')] + public function multipleAttributes(): void { + } +} + class BruteForceMiddlewareTest extends TestCase { - /** @var ControllerMethodReflector|\PHPUnit\Framework\MockObject\MockObject */ + /** @var ControllerMethodReflector */ private $reflector; - /** @var Throttler|\PHPUnit\Framework\MockObject\MockObject */ + /** @var IThrottler|\PHPUnit\Framework\MockObject\MockObject */ private $throttler; /** @var IRequest|\PHPUnit\Framework\MockObject\MockObject */ private $request; - /** @var BruteForceMiddleware */ - private $bruteForceMiddleware; + /** @var LoggerInterface|\PHPUnit\Framework\MockObject\MockObject */ + private $logger; + private BruteForceMiddleware $bruteForceMiddleware; protected function setUp(): void { parent::setUp(); - $this->reflector = $this->createMock(ControllerMethodReflector::class); - $this->throttler = $this->createMock(Throttler::class); + $this->reflector = new ControllerMethodReflector(); + $this->throttler = $this->createMock(IThrottler::class); $this->request = $this->createMock(IRequest::class); + $this->logger = $this->createMock(LoggerInterface::class); $this->bruteForceMiddleware = new BruteForceMiddleware( $this->reflector, $this->throttler, - $this->request + $this->request, + $this->logger, ); } - public function testBeforeControllerWithAnnotation() { - $this->reflector - ->expects($this->once()) - ->method('hasAnnotation') - ->with('BruteForceProtection') - ->willReturn(true); - $this->reflector - ->expects($this->once()) - ->method('getAnnotationParameter') - ->with('BruteForceProtection', 'action') - ->willReturn('login'); + public function testBeforeControllerWithAnnotation(): void { $this->request ->expects($this->once()) ->method('getRemoteAddress') @@ -73,20 +74,51 @@ class BruteForceMiddlewareTest extends TestCase { ->method('sleepDelayOrThrowOnMax') ->with('127.0.0.1', 'login'); - /** @var Controller|\PHPUnit\Framework\MockObject\MockObject $controller */ - $controller = $this->createMock(Controller::class); - $this->bruteForceMiddleware->beforeController($controller, 'testMethod'); + $controller = new TestController('test', $this->request); + $this->reflector->reflect($controller, 'testMethodWithAnnotation'); + $this->bruteForceMiddleware->beforeController($controller, 'testMethodWithAnnotation'); } - public function testBeforeControllerWithoutAnnotation() { - $this->reflector + public function testBeforeControllerWithSingleAttribute(): void { + $this->request ->expects($this->once()) - ->method('hasAnnotation') - ->with('BruteForceProtection') - ->willReturn(false); - $this->reflector - ->expects($this->never()) - ->method('getAnnotationParameter'); + ->method('getRemoteAddress') + ->willReturn('::1'); + $this->throttler + ->expects($this->once()) + ->method('sleepDelayOrThrowOnMax') + ->with('::1', 'single'); + + $controller = new TestController('test', $this->request); + $this->reflector->reflect($controller, 'singleAttribute'); + $this->bruteForceMiddleware->beforeController($controller, 'singleAttribute'); + } + + public function testBeforeControllerWithMultipleAttributes(): void { + $this->request + ->expects($this->once()) + ->method('getRemoteAddress') + ->willReturn('::1'); + + $calls = [ + ['::1', 'first'], + ['::1', 'second'], + ]; + $this->throttler + ->expects($this->exactly(2)) + ->method('sleepDelayOrThrowOnMax') + ->willReturnCallback(function () use (&$calls) { + $expected = array_shift($calls); + $this->assertEquals($expected, func_get_args()); + return 0; + }); + + $controller = new TestController('test', $this->request); + $this->reflector->reflect($controller, 'multipleAttributes'); + $this->bruteForceMiddleware->beforeController($controller, 'multipleAttributes'); + } + + public function testBeforeControllerWithoutAnnotation(): void { $this->request ->expects($this->never()) ->method('getRemoteAddress'); @@ -94,19 +126,14 @@ class BruteForceMiddlewareTest extends TestCase { ->expects($this->never()) ->method('sleepDelayOrThrowOnMax'); - /** @var Controller|\PHPUnit\Framework\MockObject\MockObject $controller */ - $controller = $this->createMock(Controller::class); - $this->bruteForceMiddleware->beforeController($controller, 'testMethod'); + $controller = new TestController('test', $this->request); + $this->reflector->reflect($controller, 'testMethodWithoutAnnotation'); + $this->bruteForceMiddleware->beforeController($controller, 'testMethodWithoutAnnotation'); } - public function testAfterControllerWithAnnotationAndThrottledRequest() { + public function testAfterControllerWithAnnotationAndThrottledRequest(): void { /** @var Response|\PHPUnit\Framework\MockObject\MockObject $response */ $response = $this->createMock(Response::class); - $this->reflector - ->expects($this->once()) - ->method('hasAnnotation') - ->with('BruteForceProtection') - ->willReturn(true); $response ->expects($this->once()) ->method('isThrottled') @@ -115,79 +142,187 @@ class BruteForceMiddlewareTest extends TestCase { ->expects($this->once()) ->method('getThrottleMetadata') ->willReturn([]); - $this->reflector - ->expects($this->once()) - ->method('getAnnotationParameter') - ->with('BruteForceProtection', 'action') - ->willReturn('login'); $this->request ->expects($this->once()) ->method('getRemoteAddress') ->willReturn('127.0.0.1'); $this->throttler ->expects($this->once()) - ->method('sleepDelay') + ->method('sleepDelayOrThrowOnMax') ->with('127.0.0.1', 'login'); $this->throttler ->expects($this->once()) ->method('registerAttempt') ->with('login', '127.0.0.1'); - /** @var Controller|\PHPUnit\Framework\MockObject\MockObject $controller */ - $controller = $this->createMock(Controller::class); - $this->bruteForceMiddleware->afterController($controller, 'testMethod', $response); + $controller = new TestController('test', $this->request); + $this->reflector->reflect($controller, 'testMethodWithAnnotation'); + $this->bruteForceMiddleware->afterController($controller, 'testMethodWithAnnotation', $response); } - public function testAfterControllerWithAnnotationAndNotThrottledRequest() { + public function testAfterControllerWithAnnotationAndNotThrottledRequest(): void { /** @var Response|\PHPUnit\Framework\MockObject\MockObject $response */ $response = $this->createMock(Response::class); - $this->reflector - ->expects($this->once()) - ->method('hasAnnotation') - ->with('BruteForceProtection') - ->willReturn(true); $response ->expects($this->once()) ->method('isThrottled') ->willReturn(false); - $this->reflector - ->expects($this->never()) - ->method('getAnnotationParameter'); $this->request ->expects($this->never()) ->method('getRemoteAddress'); $this->throttler ->expects($this->never()) - ->method('sleepDelay'); + ->method('sleepDelayOrThrowOnMax'); $this->throttler ->expects($this->never()) ->method('registerAttempt'); - /** @var Controller|\PHPUnit\Framework\MockObject\MockObject $controller */ - $controller = $this->createMock(Controller::class); - $this->bruteForceMiddleware->afterController($controller, 'testMethod', $response); + $controller = new TestController('test', $this->request); + $this->reflector->reflect($controller, 'testMethodWithAnnotation'); + $this->bruteForceMiddleware->afterController($controller, 'testMethodWithAnnotation', $response); } - public function testAfterControllerWithoutAnnotation() { - $this->reflector + public function testAfterControllerWithSingleAttribute(): void { + /** @var Response|\PHPUnit\Framework\MockObject\MockObject $response */ + $response = $this->createMock(Response::class); + $response ->expects($this->once()) - ->method('hasAnnotation') - ->with('BruteForceProtection') - ->willReturn(false); - $this->reflector + ->method('isThrottled') + ->willReturn(true); + $response + ->expects($this->once()) + ->method('getThrottleMetadata') + ->willReturn([]); + + $this->request + ->expects($this->once()) + ->method('getRemoteAddress') + ->willReturn('::1'); + $this->throttler + ->expects($this->once()) + ->method('sleepDelayOrThrowOnMax') + ->with('::1', 'single'); + $this->throttler + ->expects($this->once()) + ->method('registerAttempt') + ->with('single', '::1'); + + $controller = new TestController('test', $this->request); + $this->reflector->reflect($controller, 'singleAttribute'); + $this->bruteForceMiddleware->afterController($controller, 'singleAttribute', $response); + } + + public function testAfterControllerWithMultipleAttributesGeneralMatch(): void { + /** @var Response|\PHPUnit\Framework\MockObject\MockObject $response */ + $response = $this->createMock(Response::class); + $response + ->expects($this->once()) + ->method('isThrottled') + ->willReturn(true); + $response + ->expects($this->once()) + ->method('getThrottleMetadata') + ->willReturn([]); + + $this->request + ->expects($this->once()) + ->method('getRemoteAddress') + ->willReturn('::1'); + + $sleepCalls = [ + ['::1', 'first'], + ['::1', 'second'], + ]; + $this->throttler + ->expects($this->exactly(2)) + ->method('sleepDelayOrThrowOnMax') + ->willReturnCallback(function () use (&$sleepCalls) { + $expected = array_shift($sleepCalls); + $this->assertEquals($expected, func_get_args()); + return 0; + }); + + $attemptCalls = [ + ['first', '::1', []], + ['second', '::1', []], + ]; + $this->throttler + ->expects($this->exactly(2)) + ->method('registerAttempt') + ->willReturnCallback(function () use (&$attemptCalls): void { + $expected = array_shift($attemptCalls); + $this->assertEquals($expected, func_get_args()); + }); + + $controller = new TestController('test', $this->request); + $this->reflector->reflect($controller, 'multipleAttributes'); + $this->bruteForceMiddleware->afterController($controller, 'multipleAttributes', $response); + } + + public function testAfterControllerWithMultipleAttributesSpecificMatch(): void { + /** @var Response|\PHPUnit\Framework\MockObject\MockObject $response */ + $response = $this->createMock(Response::class); + $response + ->expects($this->once()) + ->method('isThrottled') + ->willReturn(true); + $response + ->expects($this->once()) + ->method('getThrottleMetadata') + ->willReturn(['action' => 'second']); + + $this->request + ->expects($this->once()) + ->method('getRemoteAddress') + ->willReturn('::1'); + $this->throttler + ->expects($this->once()) + ->method('sleepDelayOrThrowOnMax') + ->with('::1', 'second'); + $this->throttler + ->expects($this->once()) + ->method('registerAttempt') + ->with('second', '::1'); + + $controller = new TestController('test', $this->request); + $this->reflector->reflect($controller, 'multipleAttributes'); + $this->bruteForceMiddleware->afterController($controller, 'multipleAttributes', $response); + } + + public function testAfterControllerWithoutAnnotation(): void { + $this->request + ->expects($this->never()) + ->method('getRemoteAddress'); + $this->throttler ->expects($this->never()) - ->method('getAnnotationParameter'); + ->method('sleepDelayOrThrowOnMax'); + + $controller = new TestController('test', $this->request); + $this->reflector->reflect($controller, 'testMethodWithoutAnnotation'); + /** @var Response|\PHPUnit\Framework\MockObject\MockObject $response */ + $response = $this->createMock(Response::class); + $this->bruteForceMiddleware->afterController($controller, 'testMethodWithoutAnnotation', $response); + } + + public function testAfterControllerWithThrottledResponseButUnhandled(): void { $this->request ->expects($this->never()) ->method('getRemoteAddress'); $this->throttler ->expects($this->never()) - ->method('sleepDelay'); + ->method('sleepDelayOrThrowOnMax'); - /** @var Controller|\PHPUnit\Framework\MockObject\MockObject $controller */ - $controller = $this->createMock(Controller::class); + $controller = new TestController('test', $this->request); + $this->reflector->reflect($controller, 'testMethodWithoutAnnotation'); /** @var Response|\PHPUnit\Framework\MockObject\MockObject $response */ $response = $this->createMock(Response::class); - $this->bruteForceMiddleware->afterController($controller, 'testMethod', $response); + $response->method('isThrottled') + ->willReturn(true); + + $this->logger->expects($this->once()) + ->method('debug') + ->with('Response for Test\AppFramework\Middleware\Security\TestController::testMethodWithoutAnnotation got bruteforce throttled but has no annotation nor attribute defined.'); + + $this->bruteForceMiddleware->afterController($controller, 'testMethodWithoutAnnotation', $response); } } diff --git a/tests/lib/AppFramework/Middleware/Security/CORSMiddlewareTest.php b/tests/lib/AppFramework/Middleware/Security/CORSMiddlewareTest.php index cc6c74c16c4..c325ae638fb 100644 --- a/tests/lib/AppFramework/Middleware/Security/CORSMiddlewareTest.php +++ b/tests/lib/AppFramework/Middleware/Security/CORSMiddlewareTest.php @@ -1,51 +1,60 @@ <?php + /** - * ownCloud - App Framework - * - * This file is licensed under the Affero General Public License version 3 or - * later. See the COPYING file. - * - * @author Bernhard Posselt <dev@bernhard-posselt.com> - * @copyright Bernhard Posselt 2014 + * SPDX-FileCopyrightText: 2016-2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2014-2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace Test\AppFramework\Middleware\Security; use OC\AppFramework\Http\Request; use OC\AppFramework\Middleware\Security\CORSMiddleware; use OC\AppFramework\Middleware\Security\Exceptions\SecurityException; use OC\AppFramework\Utility\ControllerMethodReflector; -use OC\Security\Bruteforce\Throttler; +use OC\Authentication\Exceptions\PasswordLoginForbiddenException; use OC\User\Session; -use OCP\AppFramework\Controller; use OCP\AppFramework\Http\JSONResponse; use OCP\AppFramework\Http\Response; use OCP\IConfig; +use OCP\IRequest; use OCP\IRequestId; +use OCP\Security\Bruteforce\IThrottler; +use PHPUnit\Framework\MockObject\MockObject; +use Psr\Log\LoggerInterface; +use Test\AppFramework\Middleware\Security\Mock\CORSMiddlewareController; class CORSMiddlewareTest extends \Test\TestCase { - /** @var ControllerMethodReflector */ private $reflector; - /** @var Session|\PHPUnit\Framework\MockObject\MockObject */ + /** @var Session|MockObject */ private $session; - /** @var Throttler */ + /** @var IThrottler|MockObject */ private $throttler; - /** @var Controller */ + /** @var CORSMiddlewareController */ private $controller; + private LoggerInterface $logger; protected function setUp(): void { parent::setUp(); $this->reflector = new ControllerMethodReflector(); $this->session = $this->createMock(Session::class); - $this->throttler = $this->createMock(Throttler::class); - $this->controller = $this->createMock(Controller::class); + $this->throttler = $this->createMock(IThrottler::class); + $this->logger = $this->createMock(LoggerInterface::class); + $this->controller = new CORSMiddlewareController( + 'test', + $this->createMock(IRequest::class) + ); + } + + public static function dataSetCORSAPIHeader(): array { + return [ + ['testSetCORSAPIHeader'], + ['testSetCORSAPIHeaderAttribute'], + ]; } - /** - * @CORS - */ - public function testSetCORSAPIHeader() { + #[\PHPUnit\Framework\Attributes\DataProvider('dataSetCORSAPIHeader')] + public function testSetCORSAPIHeader(string $method): void { $request = new Request( [ 'server' => [ @@ -55,16 +64,15 @@ class CORSMiddlewareTest extends \Test\TestCase { $this->createMock(IRequestId::class), $this->createMock(IConfig::class) ); - $this->reflector->reflect($this, __FUNCTION__); - $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler); + $this->reflector->reflect($this->controller, $method); + $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler, $this->logger); - $response = $middleware->afterController($this->controller, __FUNCTION__, new Response()); + $response = $middleware->afterController($this->controller, $method, new Response()); $headers = $response->getHeaders(); $this->assertEquals('test', $headers['Access-Control-Allow-Origin']); } - - public function testNoAnnotationNoCORSHEADER() { + public function testNoAnnotationNoCORSHEADER(): void { $request = new Request( [ 'server' => [ @@ -74,37 +82,45 @@ class CORSMiddlewareTest extends \Test\TestCase { $this->createMock(IRequestId::class), $this->createMock(IConfig::class) ); - $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler); + $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler, $this->logger); $response = $middleware->afterController($this->controller, __FUNCTION__, new Response()); $headers = $response->getHeaders(); $this->assertFalse(array_key_exists('Access-Control-Allow-Origin', $headers)); } + public static function dataNoOriginHeaderNoCORSHEADER(): array { + return [ + ['testNoOriginHeaderNoCORSHEADER'], + ['testNoOriginHeaderNoCORSHEADERAttribute'], + ]; + } - /** - * @CORS - */ - public function testNoOriginHeaderNoCORSHEADER() { + #[\PHPUnit\Framework\Attributes\DataProvider('dataNoOriginHeaderNoCORSHEADER')] + public function testNoOriginHeaderNoCORSHEADER(string $method): void { $request = new Request( [], $this->createMock(IRequestId::class), $this->createMock(IConfig::class) ); - $this->reflector->reflect($this, __FUNCTION__); - $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler); + $this->reflector->reflect($this->controller, $method); + $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler, $this->logger); - $response = $middleware->afterController($this->controller, __FUNCTION__, new Response()); + $response = $middleware->afterController($this->controller, $method, new Response()); $headers = $response->getHeaders(); $this->assertFalse(array_key_exists('Access-Control-Allow-Origin', $headers)); } + public static function dataCorsIgnoredIfWithCredentialsHeaderPresent(): array { + return [ + ['testCorsIgnoredIfWithCredentialsHeaderPresent'], + ['testCorsAttributeIgnoredIfWithCredentialsHeaderPresent'], + ]; + } - /** - * @CORS - */ - public function testCorsIgnoredIfWithCredentialsHeaderPresent() { - $this->expectException(\OC\AppFramework\Middleware\Security\Exceptions\SecurityException::class); + #[\PHPUnit\Framework\Attributes\DataProvider('dataCorsIgnoredIfWithCredentialsHeaderPresent')] + public function testCorsIgnoredIfWithCredentialsHeaderPresent(string $method): void { + $this->expectException(SecurityException::class); $request = new Request( [ @@ -115,41 +131,87 @@ class CORSMiddlewareTest extends \Test\TestCase { $this->createMock(IRequestId::class), $this->createMock(IConfig::class) ); - $this->reflector->reflect($this, __FUNCTION__); - $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler); + $this->reflector->reflect($this->controller, $method); + $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler, $this->logger); $response = new Response(); $response->addHeader('AcCess-control-Allow-Credentials ', 'TRUE'); - $middleware->afterController($this->controller, __FUNCTION__, $response); + $middleware->afterController($this->controller, $method, $response); + } + + public static function dataNoCORSOnAnonymousPublicPage(): array { + return [ + ['testNoCORSOnAnonymousPublicPage'], + ['testNoCORSOnAnonymousPublicPageAttribute'], + ['testNoCORSAttributeOnAnonymousPublicPage'], + ['testNoCORSAttributeOnAnonymousPublicPageAttribute'], + ]; } - /** - * @CORS - * @PublicPage - */ - public function testNoCORSShouldAllowCookieAuth() { + #[\PHPUnit\Framework\Attributes\DataProvider('dataNoCORSOnAnonymousPublicPage')] + public function testNoCORSOnAnonymousPublicPage(string $method): void { $request = new Request( [], $this->createMock(IRequestId::class), $this->createMock(IConfig::class) ); - $this->reflector->reflect($this, __FUNCTION__); - $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler); + $this->reflector->reflect($this->controller, $method); + $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler, $this->logger); + $this->session->expects($this->once()) + ->method('isLoggedIn') + ->willReturn(false); $this->session->expects($this->never()) ->method('logout'); $this->session->expects($this->never()) ->method('logClientIn') ->with($this->equalTo('user'), $this->equalTo('pass')) ->willReturn(true); - $this->reflector->reflect($this, __FUNCTION__); + $this->reflector->reflect($this->controller, $method); + + $middleware->beforeController($this->controller, $method); + } + + public static function dataCORSShouldNeverAllowCookieAuth(): array { + return [ + ['testCORSShouldNeverAllowCookieAuth'], + ['testCORSShouldNeverAllowCookieAuthAttribute'], + ['testCORSAttributeShouldNeverAllowCookieAuth'], + ['testCORSAttributeShouldNeverAllowCookieAuthAttribute'], + ]; + } + + #[\PHPUnit\Framework\Attributes\DataProvider('dataCORSShouldNeverAllowCookieAuth')] + public function testCORSShouldNeverAllowCookieAuth(string $method): void { + $request = new Request( + [], + $this->createMock(IRequestId::class), + $this->createMock(IConfig::class) + ); + $this->reflector->reflect($this->controller, $method); + $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler, $this->logger); + $this->session->expects($this->once()) + ->method('isLoggedIn') + ->willReturn(true); + $this->session->expects($this->once()) + ->method('logout'); + $this->session->expects($this->never()) + ->method('logClientIn') + ->with($this->equalTo('user'), $this->equalTo('pass')) + ->willReturn(true); + + $this->expectException(SecurityException::class); + $middleware->beforeController($this->controller, $method); + } - $middleware->beforeController($this->controller, __FUNCTION__); + public static function dataCORSShouldRelogin(): array { + return [ + ['testCORSShouldRelogin'], + ['testCORSAttributeShouldRelogin'], + ]; } - /** - * @CORS - */ - public function testCORSShouldRelogin() { + #[\PHPUnit\Framework\Attributes\DataProvider('dataCORSShouldRelogin')] + public function testCORSShouldRelogin(string $method): void { $request = new Request( ['server' => [ 'PHP_AUTH_USER' => 'user', @@ -164,17 +226,22 @@ class CORSMiddlewareTest extends \Test\TestCase { ->method('logClientIn') ->with($this->equalTo('user'), $this->equalTo('pass')) ->willReturn(true); - $this->reflector->reflect($this, __FUNCTION__); - $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler); + $this->reflector->reflect($this->controller, $method); + $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler, $this->logger); - $middleware->beforeController($this->controller, __FUNCTION__); + $middleware->beforeController($this->controller, $method); } - /** - * @CORS - */ - public function testCORSShouldFailIfPasswordLoginIsForbidden() { - $this->expectException(\OC\AppFramework\Middleware\Security\Exceptions\SecurityException::class); + public static function dataCORSShouldFailIfPasswordLoginIsForbidden(): array { + return [ + ['testCORSShouldFailIfPasswordLoginIsForbidden'], + ['testCORSAttributeShouldFailIfPasswordLoginIsForbidden'], + ]; + } + + #[\PHPUnit\Framework\Attributes\DataProvider('dataCORSShouldFailIfPasswordLoginIsForbidden')] + public function testCORSShouldFailIfPasswordLoginIsForbidden(string $method): void { + $this->expectException(SecurityException::class); $request = new Request( ['server' => [ @@ -189,18 +256,23 @@ class CORSMiddlewareTest extends \Test\TestCase { $this->session->expects($this->once()) ->method('logClientIn') ->with($this->equalTo('user'), $this->equalTo('pass')) - ->will($this->throwException(new \OC\Authentication\Exceptions\PasswordLoginForbiddenException)); - $this->reflector->reflect($this, __FUNCTION__); - $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler); + ->willThrowException(new PasswordLoginForbiddenException); + $this->reflector->reflect($this->controller, $method); + $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler, $this->logger); - $middleware->beforeController($this->controller, __FUNCTION__); + $middleware->beforeController($this->controller, $method); } - /** - * @CORS - */ - public function testCORSShouldNotAllowCookieAuth() { - $this->expectException(\OC\AppFramework\Middleware\Security\Exceptions\SecurityException::class); + public static function dataCORSShouldNotAllowCookieAuth(): array { + return [ + ['testCORSShouldNotAllowCookieAuth'], + ['testCORSAttributeShouldNotAllowCookieAuth'], + ]; + } + + #[\PHPUnit\Framework\Attributes\DataProvider('dataCORSShouldNotAllowCookieAuth')] + public function testCORSShouldNotAllowCookieAuth(string $method): void { + $this->expectException(SecurityException::class); $request = new Request( ['server' => [ @@ -216,13 +288,13 @@ class CORSMiddlewareTest extends \Test\TestCase { ->method('logClientIn') ->with($this->equalTo('user'), $this->equalTo('pass')) ->willReturn(false); - $this->reflector->reflect($this, __FUNCTION__); - $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler); + $this->reflector->reflect($this->controller, $method); + $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler, $this->logger); - $middleware->beforeController($this->controller, __FUNCTION__); + $middleware->beforeController($this->controller, $method); } - public function testAfterExceptionWithSecurityExceptionNoStatus() { + public function testAfterExceptionWithSecurityExceptionNoStatus(): void { $request = new Request( ['server' => [ 'PHP_AUTH_USER' => 'user', @@ -231,14 +303,14 @@ class CORSMiddlewareTest extends \Test\TestCase { $this->createMock(IRequestId::class), $this->createMock(IConfig::class) ); - $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler); + $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler, $this->logger); $response = $middleware->afterException($this->controller, __FUNCTION__, new SecurityException('A security exception')); $expected = new JSONResponse(['message' => 'A security exception'], 500); $this->assertEquals($expected, $response); } - public function testAfterExceptionWithSecurityExceptionWithStatus() { + public function testAfterExceptionWithSecurityExceptionWithStatus(): void { $request = new Request( ['server' => [ 'PHP_AUTH_USER' => 'user', @@ -247,15 +319,14 @@ class CORSMiddlewareTest extends \Test\TestCase { $this->createMock(IRequestId::class), $this->createMock(IConfig::class) ); - $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler); + $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler, $this->logger); $response = $middleware->afterException($this->controller, __FUNCTION__, new SecurityException('A security exception', 501)); $expected = new JSONResponse(['message' => 'A security exception'], 501); $this->assertEquals($expected, $response); } - - public function testAfterExceptionWithRegularException() { + public function testAfterExceptionWithRegularException(): void { $this->expectException(\Exception::class); $this->expectExceptionMessage('A regular exception'); @@ -267,7 +338,7 @@ class CORSMiddlewareTest extends \Test\TestCase { $this->createMock(IRequestId::class), $this->createMock(IConfig::class) ); - $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler); + $middleware = new CORSMiddleware($request, $this->reflector, $this->session, $this->throttler, $this->logger); $middleware->afterException($this->controller, __FUNCTION__, new \Exception('A regular exception')); } } diff --git a/tests/lib/AppFramework/Middleware/Security/CSPMiddlewareTest.php b/tests/lib/AppFramework/Middleware/Security/CSPMiddlewareTest.php index 2dfb04e138e..b0b41b27cb9 100644 --- a/tests/lib/AppFramework/Middleware/Security/CSPMiddlewareTest.php +++ b/tests/lib/AppFramework/Middleware/Security/CSPMiddlewareTest.php @@ -2,25 +2,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\AppFramework\Middleware\Security; @@ -29,24 +12,19 @@ use OC\AppFramework\Middleware\Security\CSPMiddleware; use OC\Security\CSP\ContentSecurityPolicy; use OC\Security\CSP\ContentSecurityPolicyManager; use OC\Security\CSP\ContentSecurityPolicyNonceManager; -use OC\Security\CSRF\CsrfToken; -use OC\Security\CSRF\CsrfTokenManager; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\EmptyContentSecurityPolicy; use OCP\AppFramework\Http\Response; use PHPUnit\Framework\MockObject\MockObject; class CSPMiddlewareTest extends \Test\TestCase { - - /** @var CSPMiddleware|MockObject */ + /** @var CSPMiddleware&MockObject */ private $middleware; - /** @var Controller|MockObject */ + /** @var Controller&MockObject */ private $controller; - /** @var ContentSecurityPolicyManager|MockObject */ + /** @var ContentSecurityPolicyManager&MockObject */ private $contentSecurityPolicyManager; - /** @var CsrfTokenManager|MockObject */ - private $csrfTokenManager; - /** @var ContentSecurityPolicyNonceManager|MockObject */ + /** @var ContentSecurityPolicyNonceManager&MockObject */ private $cspNonceManager; protected function setUp(): void { @@ -54,16 +32,14 @@ class CSPMiddlewareTest extends \Test\TestCase { $this->controller = $this->createMock(Controller::class); $this->contentSecurityPolicyManager = $this->createMock(ContentSecurityPolicyManager::class); - $this->csrfTokenManager = $this->createMock(CsrfTokenManager::class); $this->cspNonceManager = $this->createMock(ContentSecurityPolicyNonceManager::class); $this->middleware = new CSPMiddleware( $this->contentSecurityPolicyManager, $this->cspNonceManager, - $this->csrfTokenManager ); } - public function testAfterController() { + public function testAfterController(): void { $this->cspNonceManager ->expects($this->once()) ->method('browserSupportsCspV3') @@ -95,7 +71,7 @@ class CSPMiddlewareTest extends \Test\TestCase { $this->middleware->afterController($this->controller, 'test', $response); } - public function testAfterControllerEmptyCSP() { + public function testAfterControllerEmptyCSP(): void { $response = $this->createMock(Response::class); $emptyPolicy = new EmptyContentSecurityPolicy(); $response->expects($this->any()) @@ -107,19 +83,15 @@ class CSPMiddlewareTest extends \Test\TestCase { $this->middleware->afterController($this->controller, 'test', $response); } - public function testAfterControllerWithContentSecurityPolicy3Support() { + public function testAfterControllerWithContentSecurityPolicy3Support(): void { $this->cspNonceManager ->expects($this->once()) ->method('browserSupportsCspV3') ->willReturn(true); - $token = $this->createMock(CsrfToken::class); - $token - ->expects($this->once()) - ->method('getEncryptedValue') - ->willReturn('MyEncryptedToken'); - $this->csrfTokenManager + $token = base64_encode('the-nonce'); + $this->cspNonceManager ->expects($this->once()) - ->method('getToken') + ->method('getNonce') ->willReturn($token); $response = $this->createMock(Response::class); $defaultPolicy = new ContentSecurityPolicy(); diff --git a/tests/lib/AppFramework/Middleware/Security/FeaturePolicyMiddlewareTest.php b/tests/lib/AppFramework/Middleware/Security/FeaturePolicyMiddlewareTest.php index 0a4b3c4f34c..55a70d4c040 100644 --- a/tests/lib/AppFramework/Middleware/Security/FeaturePolicyMiddlewareTest.php +++ b/tests/lib/AppFramework/Middleware/Security/FeaturePolicyMiddlewareTest.php @@ -2,25 +2,8 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\AppFramework\Middleware\Security; @@ -34,7 +17,6 @@ use OCP\AppFramework\Http\Response; use PHPUnit\Framework\MockObject\MockObject; class FeaturePolicyMiddlewareTest extends \Test\TestCase { - /** @var FeaturePolicyMiddleware|MockObject */ private $middleware; /** @var Controller|MockObject */ @@ -52,7 +34,7 @@ class FeaturePolicyMiddlewareTest extends \Test\TestCase { ); } - public function testAfterController() { + public function testAfterController(): void { $response = $this->createMock(Response::class); $defaultPolicy = new FeaturePolicy(); $defaultPolicy->addAllowedCameraDomain('defaultpolicy'); @@ -74,7 +56,7 @@ class FeaturePolicyMiddlewareTest extends \Test\TestCase { $this->middleware->afterController($this->controller, 'test', $response); } - public function testAfterControllerEmptyCSP() { + public function testAfterControllerEmptyCSP(): void { $response = $this->createMock(Response::class); $emptyPolicy = new EmptyFeaturePolicy(); $response->method('getFeaturePolicy') diff --git a/tests/lib/AppFramework/Middleware/Security/Mock/CORSMiddlewareController.php b/tests/lib/AppFramework/Middleware/Security/Mock/CORSMiddlewareController.php new file mode 100644 index 00000000000..8ab3a48b62e --- /dev/null +++ b/tests/lib/AppFramework/Middleware/Security/Mock/CORSMiddlewareController.php @@ -0,0 +1,145 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace Test\AppFramework\Middleware\Security\Mock; + +use OCP\AppFramework\Controller; +use OCP\AppFramework\Http\Attribute\CORS; +use OCP\AppFramework\Http\Attribute\PublicPage; + +class CORSMiddlewareController extends Controller { + /** + * @CORS + */ + public function testSetCORSAPIHeader() { + } + + #[CORS] + public function testSetCORSAPIHeaderAttribute() { + } + + public function testNoAnnotationNoCORSHEADER() { + } + + /** + * @CORS + */ + public function testNoOriginHeaderNoCORSHEADER() { + } + + #[CORS] + public function testNoOriginHeaderNoCORSHEADERAttribute() { + } + + /** + * @CORS + */ + public function testCorsIgnoredIfWithCredentialsHeaderPresent() { + } + + #[CORS] + public function testCorsAttributeIgnoredIfWithCredentialsHeaderPresent() { + } + + /** + * CORS must not be enforced for anonymous users on public pages + * + * @CORS + * @PublicPage + */ + public function testNoCORSOnAnonymousPublicPage() { + } + + /** + * CORS must not be enforced for anonymous users on public pages + * + * @CORS + */ + #[PublicPage] + public function testNoCORSOnAnonymousPublicPageAttribute() { + } + + /** + * @PublicPage + */ + #[CORS] + public function testNoCORSAttributeOnAnonymousPublicPage() { + } + + #[CORS] + #[PublicPage] + public function testNoCORSAttributeOnAnonymousPublicPageAttribute() { + } + + /** + * @CORS + * @PublicPage + */ + public function testCORSShouldNeverAllowCookieAuth() { + } + + /** + * @CORS + */ + #[PublicPage] + public function testCORSShouldNeverAllowCookieAuthAttribute() { + } + + /** + * @PublicPage + */ + #[CORS] + public function testCORSAttributeShouldNeverAllowCookieAuth() { + } + + #[CORS] + #[PublicPage] + public function testCORSAttributeShouldNeverAllowCookieAuthAttribute() { + } + + /** + * @CORS + */ + public function testCORSShouldRelogin() { + } + + #[CORS] + public function testCORSAttributeShouldRelogin() { + } + + /** + * @CORS + */ + public function testCORSShouldFailIfPasswordLoginIsForbidden() { + } + + #[CORS] + public function testCORSAttributeShouldFailIfPasswordLoginIsForbidden() { + } + + /** + * @CORS + */ + public function testCORSShouldNotAllowCookieAuth() { + } + + #[CORS] + public function testCORSAttributeShouldNotAllowCookieAuth() { + } + + public function testAfterExceptionWithSecurityExceptionNoStatus() { + } + + public function testAfterExceptionWithSecurityExceptionWithStatus() { + } + + + public function testAfterExceptionWithRegularException() { + } +} diff --git a/tests/lib/AppFramework/Middleware/Security/Mock/NormalController.php b/tests/lib/AppFramework/Middleware/Security/Mock/NormalController.php new file mode 100644 index 00000000000..4d6778e98b9 --- /dev/null +++ b/tests/lib/AppFramework/Middleware/Security/Mock/NormalController.php @@ -0,0 +1,17 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace Test\AppFramework\Middleware\Security\Mock; + +use OCP\AppFramework\Controller; + +class NormalController extends Controller { + public function foo() { + } +} diff --git a/tests/lib/AppFramework/Middleware/Security/Mock/OCSController.php b/tests/lib/AppFramework/Middleware/Security/Mock/OCSController.php new file mode 100644 index 00000000000..93e793ecca9 --- /dev/null +++ b/tests/lib/AppFramework/Middleware/Security/Mock/OCSController.php @@ -0,0 +1,15 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace Test\AppFramework\Middleware\Security\Mock; + +class OCSController extends \OCP\AppFramework\OCSController { + public function foo() { + } +} diff --git a/tests/lib/AppFramework/Middleware/Security/Mock/PasswordConfirmationMiddlewareController.php b/tests/lib/AppFramework/Middleware/Security/Mock/PasswordConfirmationMiddlewareController.php new file mode 100644 index 00000000000..cd1cdaa49ca --- /dev/null +++ b/tests/lib/AppFramework/Middleware/Security/Mock/PasswordConfirmationMiddlewareController.php @@ -0,0 +1,38 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace Test\AppFramework\Middleware\Security\Mock; + +use OCP\AppFramework\Controller; +use OCP\AppFramework\Http\Attribute\PasswordConfirmationRequired; + +class PasswordConfirmationMiddlewareController extends Controller { + public function testNoAnnotationNorAttribute() { + } + + /** + * @TestAnnotation + */ + public function testDifferentAnnotation() { + } + + /** + * @PasswordConfirmationRequired + */ + public function testAnnotation() { + } + + #[PasswordConfirmationRequired] + public function testAttribute() { + } + + #[PasswordConfirmationRequired] + public function testSSO() { + } +} diff --git a/tests/lib/AppFramework/Middleware/Security/Mock/SecurityMiddlewareController.php b/tests/lib/AppFramework/Middleware/Security/Mock/SecurityMiddlewareController.php new file mode 100644 index 00000000000..c8f9878b0c1 --- /dev/null +++ b/tests/lib/AppFramework/Middleware/Security/Mock/SecurityMiddlewareController.php @@ -0,0 +1,171 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace Test\AppFramework\Middleware\Security\Mock; + +use OCP\AppFramework\Controller; +use OCP\AppFramework\Http\Attribute\ExAppRequired; +use OCP\AppFramework\Http\Attribute\NoAdminRequired; +use OCP\AppFramework\Http\Attribute\NoCSRFRequired; +use OCP\AppFramework\Http\Attribute\PublicPage; +use OCP\AppFramework\Http\Attribute\StrictCookiesRequired; +use OCP\AppFramework\Http\Attribute\SubAdminRequired; + +class SecurityMiddlewareController extends Controller { + /** + * @PublicPage + * @NoCSRFRequired + */ + public function testAnnotationNoCSRFRequiredPublicPage() { + } + + /** + * @NoCSRFRequired + */ + #[PublicPage] + public function testAnnotationNoCSRFRequiredAttributePublicPage() { + } + + /** + * @PublicPage + */ + #[NoCSRFRequired] + public function testAnnotationPublicPageAttributeNoCSRFRequired() { + } + + #[NoCSRFRequired] + #[PublicPage] + public function testAttributeNoCSRFRequiredPublicPage() { + } + + public function testNoAnnotationNorAttribute() { + } + + /** + * @NoCSRFRequired + */ + public function testAnnotationNoCSRFRequired() { + } + + #[NoCSRFRequired] + public function testAttributeNoCSRFRequired() { + } + + /** + * @PublicPage + */ + public function testAnnotationPublicPage() { + } + + #[PublicPage] + public function testAttributePublicPage() { + } + + /** + * @PublicPage + * @StrictCookieRequired + */ + public function testAnnotationPublicPageStrictCookieRequired() { + } + + /** + * @StrictCookieRequired + */ + #[PublicPage] + public function testAnnotationStrictCookieRequiredAttributePublicPage() { + } + + /** + * @PublicPage + */ + #[StrictCookiesRequired] + public function testAnnotationPublicPageAttributeStrictCookiesRequired() { + } + + #[PublicPage] + #[StrictCookiesRequired] + public function testAttributePublicPageStrictCookiesRequired() { + } + + /** + * @PublicPage + * @NoCSRFRequired + * @StrictCookieRequired + */ + public function testAnnotationNoCSRFRequiredPublicPageStrictCookieRequired() { + } + + #[NoCSRFRequired] + #[PublicPage] + #[StrictCookiesRequired] + public function testAttributeNoCSRFRequiredPublicPageStrictCookiesRequired() { + } + + /** + * @NoCSRFRequired + * @NoAdminRequired + */ + public function testAnnotationNoAdminRequiredNoCSRFRequired() { + } + + #[NoAdminRequired] + #[NoCSRFRequired] + public function testAttributeNoAdminRequiredNoCSRFRequired() { + } + + /** + * @NoCSRFRequired + * @SubAdminRequired + */ + public function testAnnotationNoCSRFRequiredSubAdminRequired() { + } + + /** + * @SubAdminRequired + */ + #[NoCSRFRequired] + public function testAnnotationNoCSRFRequiredAttributeSubAdminRequired() { + } + + /** + * @NoCSRFRequired + */ + #[SubAdminRequired] + public function testAnnotationSubAdminRequiredAttributeNoCSRFRequired() { + } + + #[NoCSRFRequired] + #[SubAdminRequired] + public function testAttributeNoCSRFRequiredSubAdminRequired() { + } + + /** + * @PublicPage + * @NoAdminRequired + * @NoCSRFRequired + */ + public function testAnnotationNoAdminRequiredNoCSRFRequiredPublicPage() { + } + + #[NoAdminRequired] + #[NoCSRFRequired] + #[PublicPage] + public function testAttributeNoAdminRequiredNoCSRFRequiredPublicPage() { + } + + /** + * @ExAppRequired + */ + public function testAnnotationExAppRequired() { + } + + #[ExAppRequired] + public function testAttributeExAppRequired() { + } +} diff --git a/tests/lib/AppFramework/Middleware/Security/PasswordConfirmationMiddlewareTest.php b/tests/lib/AppFramework/Middleware/Security/PasswordConfirmationMiddlewareTest.php index 3153d7f0b08..90e801ca471 100644 --- a/tests/lib/AppFramework/Middleware/Security/PasswordConfirmationMiddlewareTest.php +++ b/tests/lib/AppFramework/Middleware/Security/PasswordConfirmationMiddlewareTest.php @@ -1,24 +1,8 @@ <?php + /** - * @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\AppFramework\Middleware\Security; @@ -26,74 +10,90 @@ namespace Test\AppFramework\Middleware\Security; use OC\AppFramework\Middleware\Security\Exceptions\NotConfirmedException; use OC\AppFramework\Middleware\Security\PasswordConfirmationMiddleware; use OC\AppFramework\Utility\ControllerMethodReflector; -use OCP\AppFramework\Controller; +use OC\Authentication\Token\IProvider; +use OC\User\Manager; use OCP\AppFramework\Utility\ITimeFactory; +use OCP\Authentication\Token\IToken; +use OCP\IRequest; use OCP\ISession; use OCP\IUser; use OCP\IUserSession; +use Psr\Log\LoggerInterface; +use Test\AppFramework\Middleware\Security\Mock\PasswordConfirmationMiddlewareController; use Test\TestCase; class PasswordConfirmationMiddlewareTest extends TestCase { /** @var ControllerMethodReflector */ private $reflector; - /** @var ISession|\PHPUnit\Framework\MockObject\MockObject */ + /** @var ISession&\PHPUnit\Framework\MockObject\MockObject */ private $session; - /** @var IUserSession|\PHPUnit\Framework\MockObject\MockObject */ + /** @var IUserSession&\PHPUnit\Framework\MockObject\MockObject */ private $userSession; - /** @var IUser|\PHPUnit\Framework\MockObject\MockObject */ + /** @var IUser&\PHPUnit\Framework\MockObject\MockObject */ private $user; /** @var PasswordConfirmationMiddleware */ private $middleware; - /** @var Controller */ - private $contoller; - /** @var ITimeFactory|\PHPUnit\Framework\MockObject\MockObject */ + /** @var PasswordConfirmationMiddlewareController */ + private $controller; + /** @var ITimeFactory&\PHPUnit\Framework\MockObject\MockObject */ private $timeFactory; + private IProvider&\PHPUnit\Framework\MockObject\MockObject $tokenProvider; + private LoggerInterface $logger; + /** @var IRequest&\PHPUnit\Framework\MockObject\MockObject */ + private IRequest $request; + /** @var Manager&\PHPUnit\Framework\MockObject\MockObject */ + private Manager $userManager; protected function setUp(): void { $this->reflector = new ControllerMethodReflector(); $this->session = $this->createMock(ISession::class); $this->userSession = $this->createMock(IUserSession::class); $this->user = $this->createMock(IUser::class); - $this->contoller = $this->createMock(Controller::class); $this->timeFactory = $this->createMock(ITimeFactory::class); + $this->tokenProvider = $this->createMock(IProvider::class); + $this->logger = $this->createMock(LoggerInterface::class); + $this->request = $this->createMock(IRequest::class); + $this->userManager = $this->createMock(Manager::class); + $this->controller = new PasswordConfirmationMiddlewareController( + 'test', + $this->createMock(IRequest::class) + ); $this->middleware = new PasswordConfirmationMiddleware( $this->reflector, $this->session, $this->userSession, - $this->timeFactory + $this->timeFactory, + $this->tokenProvider, + $this->logger, + $this->request, + $this->userManager, ); } - public function testNoAnnotation() { - $this->reflector->reflect(__CLASS__, __FUNCTION__); + public function testNoAnnotationNorAttribute(): void { + $this->reflector->reflect($this->controller, __FUNCTION__); $this->session->expects($this->never()) ->method($this->anything()); $this->userSession->expects($this->never()) ->method($this->anything()); - $this->middleware->beforeController($this->contoller, __FUNCTION__); + $this->middleware->beforeController($this->controller, __FUNCTION__); } - /** - * @TestAnnotation - */ - public function testDifferentAnnotation() { - $this->reflector->reflect(__CLASS__, __FUNCTION__); + public function testDifferentAnnotation(): void { + $this->reflector->reflect($this->controller, __FUNCTION__); $this->session->expects($this->never()) ->method($this->anything()); $this->userSession->expects($this->never()) ->method($this->anything()); - $this->middleware->beforeController($this->contoller, __FUNCTION__); + $this->middleware->beforeController($this->controller, __FUNCTION__); } - /** - * @PasswordConfirmationRequired - * @dataProvider dataProvider - */ - public function testAnnotation($backend, $lastConfirm, $currentTime, $exception) { - $this->reflector->reflect(__CLASS__, __FUNCTION__); + #[\PHPUnit\Framework\Attributes\DataProvider('dataProvider')] + public function testAnnotation($backend, $lastConfirm, $currentTime, $exception): void { + $this->reflector->reflect($this->controller, __FUNCTION__); $this->user->method('getBackendClassName') ->willReturn($backend); @@ -107,9 +107,16 @@ class PasswordConfirmationMiddlewareTest extends TestCase { $this->timeFactory->method('getTime') ->willReturn($currentTime); + $token = $this->createMock(IToken::class); + $token->method('getScopeAsArray') + ->willReturn([]); + $this->tokenProvider->expects($this->once()) + ->method('getToken') + ->willReturn($token); + $thrown = false; try { - $this->middleware->beforeController($this->contoller, __FUNCTION__); + $this->middleware->beforeController($this->controller, __FUNCTION__); } catch (NotConfirmedException $e) { $thrown = true; } @@ -117,7 +124,42 @@ class PasswordConfirmationMiddlewareTest extends TestCase { $this->assertSame($exception, $thrown); } - public function dataProvider() { + #[\PHPUnit\Framework\Attributes\DataProvider('dataProvider')] + public function testAttribute($backend, $lastConfirm, $currentTime, $exception): void { + $this->reflector->reflect($this->controller, __FUNCTION__); + + $this->user->method('getBackendClassName') + ->willReturn($backend); + $this->userSession->method('getUser') + ->willReturn($this->user); + + $this->session->method('get') + ->with('last-password-confirm') + ->willReturn($lastConfirm); + + $this->timeFactory->method('getTime') + ->willReturn($currentTime); + + $token = $this->createMock(IToken::class); + $token->method('getScopeAsArray') + ->willReturn([]); + $this->tokenProvider->expects($this->once()) + ->method('getToken') + ->willReturn($token); + + $thrown = false; + try { + $this->middleware->beforeController($this->controller, __FUNCTION__); + } catch (NotConfirmedException $e) { + $thrown = true; + } + + $this->assertSame($exception, $thrown); + } + + + + public static function dataProvider(): array { return [ ['foo', 2000, 4000, true], ['foo', 2000, 3000, false], @@ -127,4 +169,41 @@ class PasswordConfirmationMiddlewareTest extends TestCase { ['foo', 2000, 3816, true], ]; } + + public function testSSO(): void { + static $sessionId = 'mySession1d'; + + $this->reflector->reflect($this->controller, __FUNCTION__); + + $this->user->method('getBackendClassName') + ->willReturn('fictional_backend'); + $this->userSession->method('getUser') + ->willReturn($this->user); + + $this->session->method('get') + ->with('last-password-confirm') + ->willReturn(0); + $this->session->method('getId') + ->willReturn($sessionId); + + $this->timeFactory->method('getTime') + ->willReturn(9876); + + $token = $this->createMock(IToken::class); + $token->method('getScopeAsArray') + ->willReturn([IToken::SCOPE_SKIP_PASSWORD_VALIDATION => true]); + $this->tokenProvider->expects($this->once()) + ->method('getToken') + ->with($sessionId) + ->willReturn($token); + + $thrown = false; + try { + $this->middleware->beforeController($this->controller, __FUNCTION__); + } catch (NotConfirmedException) { + $thrown = true; + } + + $this->assertSame(false, $thrown); + } } diff --git a/tests/lib/AppFramework/Middleware/Security/RateLimitingMiddlewareTest.php b/tests/lib/AppFramework/Middleware/Security/RateLimitingMiddlewareTest.php index aa713b99156..c42baadcb1c 100644 --- a/tests/lib/AppFramework/Middleware/Security/RateLimitingMiddlewareTest.php +++ b/tests/lib/AppFramework/Middleware/Security/RateLimitingMiddlewareTest.php @@ -1,91 +1,113 @@ <?php + +declare(strict_types=1); + /** - * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\AppFramework\Middleware\Security; use OC\AppFramework\Middleware\Security\RateLimitingMiddleware; use OC\AppFramework\Utility\ControllerMethodReflector; +use OC\Security\Ip\BruteforceAllowList; use OC\Security\RateLimiting\Exception\RateLimitExceededException; use OC\Security\RateLimiting\Limiter; use OCP\AppFramework\Controller; +use OCP\AppFramework\Http\Attribute\AnonRateLimit; +use OCP\AppFramework\Http\Attribute\UserRateLimit; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Http\TemplateResponse; +use OCP\IAppConfig; use OCP\IRequest; +use OCP\ISession; use OCP\IUser; use OCP\IUserSession; +use PHPUnit\Framework\MockObject\MockObject; use Test\TestCase; +class TestRateLimitController extends Controller { + /** + * @UserRateThrottle(limit=20, period=200) + * @AnonRateThrottle(limit=10, period=100) + */ + public function testMethodWithAnnotation() { + } + + /** + * @AnonRateThrottle(limit=10, period=100) + */ + public function testMethodWithAnnotationFallback() { + } + + public function testMethodWithoutAnnotation() { + } + + #[UserRateLimit(limit: 20, period: 200)] + #[AnonRateLimit(limit: 10, period: 100)] + public function testMethodWithAttributes() { + } + + #[AnonRateLimit(limit: 10, period: 100)] + public function testMethodWithAttributesFallback() { + } +} + /** * @group DB */ class RateLimitingMiddlewareTest extends TestCase { - /** @var IRequest|\PHPUnit\Framework\MockObject\MockObject */ - private $request; - /** @var IUserSession|\PHPUnit\Framework\MockObject\MockObject */ - private $userSession; - /** @var ControllerMethodReflector|\PHPUnit\Framework\MockObject\MockObject */ - private $reflector; - /** @var Limiter|\PHPUnit\Framework\MockObject\MockObject */ - private $limiter; - /** @var RateLimitingMiddleware */ - private $rateLimitingMiddleware; + private IRequest|MockObject $request; + private IUserSession|MockObject $userSession; + private ControllerMethodReflector $reflector; + private Limiter|MockObject $limiter; + private ISession|MockObject $session; + private IAppConfig|MockObject $appConfig; + private BruteforceAllowList|MockObject $bruteForceAllowList; + private RateLimitingMiddleware $rateLimitingMiddleware; protected function setUp(): void { parent::setUp(); $this->request = $this->createMock(IRequest::class); $this->userSession = $this->createMock(IUserSession::class); - $this->reflector = $this->createMock(ControllerMethodReflector::class); + $this->reflector = new ControllerMethodReflector(); $this->limiter = $this->createMock(Limiter::class); + $this->session = $this->createMock(ISession::class); + $this->appConfig = $this->createMock(IAppConfig::class); + $this->bruteForceAllowList = $this->createMock(BruteforceAllowList::class); $this->rateLimitingMiddleware = new RateLimitingMiddleware( $this->request, $this->userSession, $this->reflector, - $this->limiter + $this->limiter, + $this->session, + $this->appConfig, + $this->bruteForceAllowList, ); } - public function testBeforeControllerWithoutAnnotation() { - $this->reflector - ->expects($this->at(0)) - ->method('getAnnotationParameter') - ->with('AnonRateThrottle', 'limit') - ->willReturn(''); - $this->reflector - ->expects($this->at(1)) - ->method('getAnnotationParameter') - ->with('AnonRateThrottle', 'period') - ->willReturn(''); - $this->reflector - ->expects($this->at(2)) - ->method('getAnnotationParameter') - ->with('UserRateThrottle', 'limit') - ->willReturn(''); - $this->reflector - ->expects($this->at(3)) - ->method('getAnnotationParameter') - ->with('UserRateThrottle', 'period') - ->willReturn(''); + public function testBeforeControllerWithoutAnnotationForAnon(): void { + $this->limiter + ->expects($this->never()) + ->method('registerUserRequest'); + $this->limiter + ->expects($this->never()) + ->method('registerAnonRequest'); + + $this->userSession->expects($this->once()) + ->method('isLoggedIn') + ->willReturn(false); + + /** @var TestRateLimitController|MockObject $controller */ + $controller = $this->createMock(TestRateLimitController::class); + $this->reflector->reflect($controller, 'testMethodWithoutAnnotation'); + $this->rateLimitingMiddleware->beforeController($controller, 'testMethodWithoutAnnotation'); + } + public function testBeforeControllerWithoutAnnotationForLoggedIn(): void { $this->limiter ->expects($this->never()) ->method('registerUserRequest'); @@ -93,39 +115,79 @@ class RateLimitingMiddlewareTest extends TestCase { ->expects($this->never()) ->method('registerAnonRequest'); - /** @var Controller|\PHPUnit\Framework\MockObject\MockObject $controller */ - $controller = $this->createMock(Controller::class); - $this->rateLimitingMiddleware->beforeController($controller, 'testMethod'); + $this->userSession->expects($this->once()) + ->method('isLoggedIn') + ->willReturn(true); + + /** @var TestRateLimitController|MockObject $controller */ + $controller = $this->createMock(TestRateLimitController::class); + $this->reflector->reflect($controller, 'testMethodWithoutAnnotation'); + $this->rateLimitingMiddleware->beforeController($controller, 'testMethodWithoutAnnotation'); + } + + public function testBeforeControllerForAnon(): void { + $controller = new TestRateLimitController('test', $this->request); + + $this->request + ->method('getRemoteAddress') + ->willReturn('127.0.0.1'); + + $this->userSession + ->expects($this->once()) + ->method('isLoggedIn') + ->willReturn(false); + + $this->limiter + ->expects($this->never()) + ->method('registerUserRequest'); + $this->limiter + ->expects($this->once()) + ->method('registerAnonRequest') + ->with(get_class($controller) . '::testMethodWithAnnotation', '10', '100', '127.0.0.1'); + + $this->reflector->reflect($controller, 'testMethodWithAnnotation'); + $this->rateLimitingMiddleware->beforeController($controller, 'testMethodWithAnnotation'); + } + + public function testBeforeControllerForLoggedIn(): void { + $controller = new TestRateLimitController('test', $this->request); + /** @var IUser|MockObject $user */ + $user = $this->createMock(IUser::class); + + $this->userSession + ->expects($this->once()) + ->method('isLoggedIn') + ->willReturn(true); + $this->userSession + ->expects($this->once()) + ->method('getUser') + ->willReturn($user); + + $this->limiter + ->expects($this->never()) + ->method('registerAnonRequest'); + $this->limiter + ->expects($this->once()) + ->method('registerUserRequest') + ->with(get_class($controller) . '::testMethodWithAnnotation', '20', '200', $user); + + + $this->reflector->reflect($controller, 'testMethodWithAnnotation'); + $this->rateLimitingMiddleware->beforeController($controller, 'testMethodWithAnnotation'); } - public function testBeforeControllerForAnon() { - /** @var Controller|\PHPUnit\Framework\MockObject\MockObject $controller */ - $controller = $this->createMock(Controller::class); + public function testBeforeControllerAnonWithFallback(): void { + $controller = new TestRateLimitController('test', $this->request); $this->request ->expects($this->once()) ->method('getRemoteAddress') ->willReturn('127.0.0.1'); - $this->reflector - ->expects($this->at(0)) - ->method('getAnnotationParameter') - ->with('AnonRateThrottle', 'limit') - ->willReturn('100'); - $this->reflector - ->expects($this->at(1)) - ->method('getAnnotationParameter') - ->with('AnonRateThrottle', 'period') - ->willReturn('10'); - $this->reflector - ->expects($this->at(2)) - ->method('getAnnotationParameter') - ->with('UserRateThrottle', 'limit') - ->willReturn(''); - $this->reflector - ->expects($this->at(3)) - ->method('getAnnotationParameter') - ->with('UserRateThrottle', 'period') - ->willReturn(''); + $this->userSession + ->expects($this->once()) + ->method('isLoggedIn') + ->willReturn(true); + $this->limiter ->expects($this->never()) @@ -133,16 +195,39 @@ class RateLimitingMiddlewareTest extends TestCase { $this->limiter ->expects($this->once()) ->method('registerAnonRequest') - ->with(get_class($controller) . '::testMethod', '100', '10', '127.0.0.1'); + ->with(get_class($controller) . '::testMethodWithAnnotationFallback', '10', '100', '127.0.0.1'); + + $this->reflector->reflect($controller, 'testMethodWithAnnotationFallback'); + $this->rateLimitingMiddleware->beforeController($controller, 'testMethodWithAnnotationFallback'); + } + + public function testBeforeControllerAttributesForAnon(): void { + $controller = new TestRateLimitController('test', $this->request); + + $this->request + ->method('getRemoteAddress') + ->willReturn('127.0.0.1'); + $this->userSession + ->expects($this->once()) + ->method('isLoggedIn') + ->willReturn(false); - $this->rateLimitingMiddleware->beforeController($controller, 'testMethod'); + $this->limiter + ->expects($this->never()) + ->method('registerUserRequest'); + $this->limiter + ->expects($this->once()) + ->method('registerAnonRequest') + ->with(get_class($controller) . '::testMethodWithAttributes', '10', '100', '127.0.0.1'); + + $this->reflector->reflect($controller, 'testMethodWithAttributes'); + $this->rateLimitingMiddleware->beforeController($controller, 'testMethodWithAttributes'); } - public function testBeforeControllerForLoggedIn() { - /** @var Controller|\PHPUnit\Framework\MockObject\MockObject $controller */ - $controller = $this->createMock(Controller::class); - /** @var IUser|\PHPUnit\Framework\MockObject\MockObject $user */ + public function testBeforeControllerAttributesForLoggedIn(): void { + $controller = new TestRateLimitController('test', $this->request); + /** @var IUser|MockObject $user */ $user = $this->createMock(IUser::class); $this->userSession @@ -154,42 +239,21 @@ class RateLimitingMiddlewareTest extends TestCase { ->method('getUser') ->willReturn($user); - $this->reflector - ->expects($this->at(0)) - ->method('getAnnotationParameter') - ->with('AnonRateThrottle', 'limit') - ->willReturn(''); - $this->reflector - ->expects($this->at(1)) - ->method('getAnnotationParameter') - ->with('AnonRateThrottle', 'period') - ->willReturn(''); - $this->reflector - ->expects($this->at(2)) - ->method('getAnnotationParameter') - ->with('UserRateThrottle', 'limit') - ->willReturn('100'); - $this->reflector - ->expects($this->at(3)) - ->method('getAnnotationParameter') - ->with('UserRateThrottle', 'period') - ->willReturn('10'); - $this->limiter ->expects($this->never()) ->method('registerAnonRequest'); $this->limiter ->expects($this->once()) ->method('registerUserRequest') - ->with(get_class($controller) . '::testMethod', '100', '10', $user); + ->with(get_class($controller) . '::testMethodWithAttributes', '20', '200', $user); - $this->rateLimitingMiddleware->beforeController($controller, 'testMethod'); + $this->reflector->reflect($controller, 'testMethodWithAttributes'); + $this->rateLimitingMiddleware->beforeController($controller, 'testMethodWithAttributes'); } - public function testBeforeControllerAnonWithFallback() { - /** @var Controller|\PHPUnit\Framework\MockObject\MockObject $controller */ - $controller = $this->createMock(Controller::class); + public function testBeforeControllerAttributesAnonWithFallback(): void { + $controller = new TestRateLimitController('test', $this->request); $this->request ->expects($this->once()) ->method('getRemoteAddress') @@ -198,28 +262,8 @@ class RateLimitingMiddlewareTest extends TestCase { $this->userSession ->expects($this->once()) ->method('isLoggedIn') - ->willReturn(false); + ->willReturn(true); - $this->reflector - ->expects($this->at(0)) - ->method('getAnnotationParameter') - ->with('AnonRateThrottle', 'limit') - ->willReturn('200'); - $this->reflector - ->expects($this->at(1)) - ->method('getAnnotationParameter') - ->with('AnonRateThrottle', 'period') - ->willReturn('20'); - $this->reflector - ->expects($this->at(2)) - ->method('getAnnotationParameter') - ->with('UserRateThrottle', 'limit') - ->willReturn('100'); - $this->reflector - ->expects($this->at(3)) - ->method('getAnnotationParameter') - ->with('UserRateThrottle', 'period') - ->willReturn('10'); $this->limiter ->expects($this->never()) @@ -227,25 +271,23 @@ class RateLimitingMiddlewareTest extends TestCase { $this->limiter ->expects($this->once()) ->method('registerAnonRequest') - ->with(get_class($controller) . '::testMethod', '200', '20', '127.0.0.1'); + ->with(get_class($controller) . '::testMethodWithAttributesFallback', '10', '100', '127.0.0.1'); - $this->rateLimitingMiddleware->beforeController($controller, 'testMethod'); + $this->reflector->reflect($controller, 'testMethodWithAttributesFallback'); + $this->rateLimitingMiddleware->beforeController($controller, 'testMethodWithAttributesFallback'); } - - public function testAfterExceptionWithOtherException() { + public function testAfterExceptionWithOtherException(): void { $this->expectException(\Exception::class); $this->expectExceptionMessage('My test exception'); - /** @var Controller|\PHPUnit\Framework\MockObject\MockObject $controller */ - $controller = $this->createMock(Controller::class); + $controller = new TestRateLimitController('test', $this->request); $this->rateLimitingMiddleware->afterException($controller, 'testMethod', new \Exception('My test exception')); } - public function testAfterExceptionWithJsonBody() { - /** @var Controller|\PHPUnit\Framework\MockObject\MockObject $controller */ - $controller = $this->createMock(Controller::class); + public function testAfterExceptionWithJsonBody(): void { + $controller = new TestRateLimitController('test', $this->request); $this->request ->expects($this->once()) ->method('getHeader') @@ -258,9 +300,8 @@ class RateLimitingMiddlewareTest extends TestCase { $this->assertEquals($expected, $result); } - public function testAfterExceptionWithHtmlBody() { - /** @var Controller|\PHPUnit\Framework\MockObject\MockObject $controller */ - $controller = $this->createMock(Controller::class); + public function testAfterExceptionWithHtmlBody(): void { + $controller = new TestRateLimitController('test', $this->request); $this->request ->expects($this->once()) ->method('getHeader') diff --git a/tests/lib/AppFramework/Middleware/Security/SameSiteCookieMiddlewareTest.php b/tests/lib/AppFramework/Middleware/Security/SameSiteCookieMiddlewareTest.php index 61f5eeb5aea..7800371f68f 100644 --- a/tests/lib/AppFramework/Middleware/Security/SameSiteCookieMiddlewareTest.php +++ b/tests/lib/AppFramework/Middleware/Security/SameSiteCookieMiddlewareTest.php @@ -1,24 +1,8 @@ <?php + /** - * @copyright 2017, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\AppFramework\Middleware\Security; @@ -33,7 +17,6 @@ use OCP\AppFramework\Http; use Test\TestCase; class SameSiteCookieMiddlewareTest extends TestCase { - /** @var SameSiteCookieMiddleware */ private $middleware; @@ -51,7 +34,7 @@ class SameSiteCookieMiddlewareTest extends TestCase { $this->middleware = new SameSiteCookieMiddleware($this->request, $this->reflector); } - public function testBeforeControllerNoIndex() { + public function testBeforeControllerNoIndex(): void { $this->request->method('getScriptName') ->willReturn('/ocs/v2.php'); @@ -59,7 +42,7 @@ class SameSiteCookieMiddlewareTest extends TestCase { $this->addToAssertionCount(1); } - public function testBeforeControllerIndexHasAnnotation() { + public function testBeforeControllerIndexHasAnnotation(): void { $this->request->method('getScriptName') ->willReturn('/index.php'); @@ -71,7 +54,7 @@ class SameSiteCookieMiddlewareTest extends TestCase { $this->addToAssertionCount(1); } - public function testBeforeControllerIndexNoAnnotationPassingCheck() { + public function testBeforeControllerIndexNoAnnotationPassingCheck(): void { $this->request->method('getScriptName') ->willReturn('/index.php'); @@ -86,7 +69,7 @@ class SameSiteCookieMiddlewareTest extends TestCase { $this->addToAssertionCount(1); } - public function testBeforeControllerIndexNoAnnotationFailingCheck() { + public function testBeforeControllerIndexNoAnnotationFailingCheck(): void { $this->expectException(LaxSameSiteCookieFailedException::class); $this->request->method('getScriptName') @@ -102,7 +85,7 @@ class SameSiteCookieMiddlewareTest extends TestCase { $this->middleware->beforeController($this->createMock(Controller::class), 'foo'); } - public function testAfterExceptionNoLaxCookie() { + public function testAfterExceptionNoLaxCookie(): void { $ex = new SecurityException(); try { @@ -113,7 +96,7 @@ class SameSiteCookieMiddlewareTest extends TestCase { } } - public function testAfterExceptionLaxCookie() { + public function testAfterExceptionLaxCookie(): void { $ex = new LaxSameSiteCookieFailedException(); $this->request->method('getRequestUri') @@ -121,7 +104,7 @@ class SameSiteCookieMiddlewareTest extends TestCase { $middleware = $this->getMockBuilder(SameSiteCookieMiddleware::class) ->setConstructorArgs([$this->request, $this->reflector]) - ->setMethods(['setSameSiteCookie']) + ->onlyMethods(['setSameSiteCookie']) ->getMock(); $middleware->expects($this->once()) diff --git a/tests/lib/AppFramework/Middleware/Security/SecurityMiddlewareTest.php b/tests/lib/AppFramework/Middleware/Security/SecurityMiddlewareTest.php index 276ebe8f9ac..0c6fc21357d 100644 --- a/tests/lib/AppFramework/Middleware/Security/SecurityMiddlewareTest.php +++ b/tests/lib/AppFramework/Middleware/Security/SecurityMiddlewareTest.php @@ -1,23 +1,9 @@ <?php + /** - * @author Bernhard Posselt <dev@bernhard-posselt.com> - * @author Lukas Reschke <lukas@owncloud.com> - * - * @copyright Copyright (c) 2015, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace Test\AppFramework\Middleware\Security; @@ -26,6 +12,7 @@ use OC\AppFramework\Http; use OC\AppFramework\Http\Request; use OC\AppFramework\Middleware\Security\Exceptions\AppNotEnabledException; use OC\AppFramework\Middleware\Security\Exceptions\CrossSiteRequestForgeryException; +use OC\AppFramework\Middleware\Security\Exceptions\ExAppRequiredException; use OC\AppFramework\Middleware\Security\Exceptions\NotAdminException; use OC\AppFramework\Middleware\Security\Exceptions\NotLoggedInException; use OC\AppFramework\Middleware\Security\Exceptions\SecurityException; @@ -33,25 +20,32 @@ use OC\Appframework\Middleware\Security\Exceptions\StrictCookieMissingException; use OC\AppFramework\Middleware\Security\SecurityMiddleware; use OC\AppFramework\Utility\ControllerMethodReflector; use OC\Settings\AuthorizedGroupMapper; +use OC\User\Session; use OCP\App\IAppManager; -use OCP\AppFramework\Controller; use OCP\AppFramework\Http\JSONResponse; use OCP\AppFramework\Http\RedirectResponse; use OCP\AppFramework\Http\TemplateResponse; +use OCP\Group\ISubAdmin; use OCP\IConfig; +use OCP\IGroupManager; use OCP\IL10N; use OCP\INavigationManager; use OCP\IRequest; use OCP\IRequestId; +use OCP\ISession; use OCP\IURLGenerator; +use OCP\IUser; use OCP\IUserSession; +use OCP\Security\Ip\IRemoteAddress; use Psr\Log\LoggerInterface; +use Test\AppFramework\Middleware\Security\Mock\NormalController; +use Test\AppFramework\Middleware\Security\Mock\OCSController; +use Test\AppFramework\Middleware\Security\Mock\SecurityMiddlewareController; class SecurityMiddlewareTest extends \Test\TestCase { - /** @var SecurityMiddleware|\PHPUnit\Framework\MockObject\MockObject */ private $middleware; - /** @var Controller|\PHPUnit\Framework\MockObject\MockObject */ + /** @var SecurityMiddlewareController */ private $controller; /** @var SecurityException */ private $secException; @@ -80,13 +74,19 @@ class SecurityMiddlewareTest extends \Test\TestCase { parent::setUp(); $this->authorizedGroupMapper = $this->createMock(AuthorizedGroupMapper::class); - $this->userSession = $this->createMock(IUserSession::class); - $this->controller = $this->createMock(Controller::class); + $this->userSession = $this->createMock(Session::class); + $user = $this->createMock(IUser::class); + $user->method('getUID')->willReturn('test'); + $this->userSession->method('getUser')->willReturn($user); + $this->request = $this->createMock(IRequest::class); + $this->controller = new SecurityMiddlewareController( + 'test', + $this->request + ); $this->reader = new ControllerMethodReflector(); $this->logger = $this->createMock(LoggerInterface::class); $this->navigationManager = $this->createMock(INavigationManager::class); $this->urlGenerator = $this->createMock(IURLGenerator::class); - $this->request = $this->createMock(IRequest::class); $this->l10n = $this->createMock(IL10N::class); $this->middleware = $this->getMiddleware(true, true, false); $this->secException = new SecurityException('hey', false); @@ -98,6 +98,15 @@ class SecurityMiddlewareTest extends \Test\TestCase { $this->appManager->expects($this->any()) ->method('isEnabledForUser') ->willReturn($isAppEnabledForUser); + $remoteIpAddress = $this->createMock(IRemoteAddress::class); + $remoteIpAddress->method('allowsAdminActions')->willReturn(true); + + $groupManager = $this->createMock(IGroupManager::class); + $groupManager->method('isAdmin') + ->willReturn($isAdminUser); + $subAdminManager = $this->createMock(ISubAdmin::class); + $subAdminManager->method('isSubAdmin') + ->willReturn($isSubAdmin); return new SecurityMiddleware( $this->request, @@ -107,27 +116,93 @@ class SecurityMiddlewareTest extends \Test\TestCase { $this->logger, 'files', $isLoggedIn, - $isAdminUser, - $isSubAdmin, + $groupManager, + $subAdminManager, $this->appManager, $this->l10n, $this->authorizedGroupMapper, - $this->userSession + $this->userSession, + $remoteIpAddress ); } + public static function dataNoCSRFRequiredPublicPage(): array { + return [ + ['testAnnotationNoCSRFRequiredPublicPage'], + ['testAnnotationNoCSRFRequiredAttributePublicPage'], + ['testAnnotationPublicPageAttributeNoCSRFRequired'], + ['testAttributeNoCSRFRequiredPublicPage'], + ]; + } - /** - * @PublicPage - * @NoCSRFRequired - */ - public function testSetNavigationEntry() { + public static function dataPublicPage(): array { + return [ + ['testAnnotationPublicPage'], + ['testAttributePublicPage'], + ]; + } + + public static function dataNoCSRFRequired(): array { + return [ + ['testAnnotationNoCSRFRequired'], + ['testAttributeNoCSRFRequired'], + ]; + } + + public static function dataPublicPageStrictCookieRequired(): array { + return [ + ['testAnnotationPublicPageStrictCookieRequired'], + ['testAnnotationStrictCookieRequiredAttributePublicPage'], + ['testAnnotationPublicPageAttributeStrictCookiesRequired'], + ['testAttributePublicPageStrictCookiesRequired'], + ]; + } + + public static function dataNoCSRFRequiredPublicPageStrictCookieRequired(): array { + return [ + ['testAnnotationNoCSRFRequiredPublicPageStrictCookieRequired'], + ['testAttributeNoCSRFRequiredPublicPageStrictCookiesRequired'], + ]; + } + + public static function dataNoAdminRequiredNoCSRFRequired(): array { + return [ + ['testAnnotationNoAdminRequiredNoCSRFRequired'], + ['testAttributeNoAdminRequiredNoCSRFRequired'], + ]; + } + + public static function dataNoAdminRequiredNoCSRFRequiredPublicPage(): array { + return [ + ['testAnnotationNoAdminRequiredNoCSRFRequiredPublicPage'], + ['testAttributeNoAdminRequiredNoCSRFRequiredPublicPage'], + ]; + } + + public static function dataNoCSRFRequiredSubAdminRequired(): array { + return [ + ['testAnnotationNoCSRFRequiredSubAdminRequired'], + ['testAnnotationNoCSRFRequiredAttributeSubAdminRequired'], + ['testAnnotationSubAdminRequiredAttributeNoCSRFRequired'], + ['testAttributeNoCSRFRequiredSubAdminRequired'], + ]; + } + + public static function dataExAppRequired(): array { + return [ + ['testAnnotationExAppRequired'], + ['testAttributeExAppRequired'], + ]; + } + + #[\PHPUnit\Framework\Attributes\DataProvider('dataNoCSRFRequiredPublicPage')] + public function testSetNavigationEntry(string $method): void { $this->navigationManager->expects($this->once()) ->method('setActiveEntry') ->with($this->equalTo('files')); - $this->reader->reflect(__CLASS__, __FUNCTION__); - $this->middleware->beforeController($this->controller, __FUNCTION__); + $this->reader->reflect($this->controller, $method); + $this->middleware->beforeController($this->controller, $method); } @@ -147,7 +222,7 @@ class SecurityMiddlewareTest extends \Test\TestCase { $sec = $this->getMiddleware($isLoggedIn, $isAdminUser, false); try { - $this->reader->reflect(__CLASS__, $method); + $this->reader->reflect($this->controller, $method); $sec->beforeController($this->controller, $method); } catch (SecurityException $ex) { $this->assertEquals($status, $ex->getCode()); @@ -160,75 +235,63 @@ class SecurityMiddlewareTest extends \Test\TestCase { } } - public function testAjaxStatusLoggedInCheck() { + public function testAjaxStatusLoggedInCheck(): void { $this->ajaxExceptionStatus( - __FUNCTION__, + 'testNoAnnotationNorAttribute', 'isLoggedIn', Http::STATUS_UNAUTHORIZED ); } - /** - * @NoCSRFRequired - */ - public function testAjaxNotAdminCheck() { + #[\PHPUnit\Framework\Attributes\DataProvider('dataNoCSRFRequired')] + public function testAjaxNotAdminCheck(string $method): void { $this->ajaxExceptionStatus( - __FUNCTION__, + $method, 'isAdminUser', Http::STATUS_FORBIDDEN ); } - /** - * @PublicPage - */ - public function testAjaxStatusCSRFCheck() { + #[\PHPUnit\Framework\Attributes\DataProvider('dataPublicPage')] + public function testAjaxStatusCSRFCheck(string $method): void { $this->ajaxExceptionStatus( - __FUNCTION__, + $method, 'passesCSRFCheck', Http::STATUS_PRECONDITION_FAILED ); } - /** - * @PublicPage - * @NoCSRFRequired - */ - public function testAjaxStatusAllGood() { + #[\PHPUnit\Framework\Attributes\DataProvider('dataNoCSRFRequiredPublicPage')] + public function testAjaxStatusAllGood(string $method): void { $this->ajaxExceptionStatus( - __FUNCTION__, + $method, 'isLoggedIn', 0 ); $this->ajaxExceptionStatus( - __FUNCTION__, + $method, 'isAdminUser', 0 ); $this->ajaxExceptionStatus( - __FUNCTION__, + $method, 'passesCSRFCheck', 0 ); } - - /** - * @PublicPage - * @NoCSRFRequired - */ - public function testNoChecks() { + #[\PHPUnit\Framework\Attributes\DataProvider('dataNoCSRFRequiredPublicPage')] + public function testNoChecks(string $method): void { $this->request->expects($this->never()) ->method('passesCSRFCheck') ->willReturn(false); $sec = $this->getMiddleware(false, false, false); - $this->reader->reflect(__CLASS__, __FUNCTION__); - $sec->beforeController($this->controller, __FUNCTION__); + $this->reader->reflect($this->controller, $method); + $sec->beforeController($this->controller, $method); } - /** * @param string $method * @param string $expects @@ -251,16 +314,14 @@ class SecurityMiddlewareTest extends \Test\TestCase { $this->addToAssertionCount(1); } - $this->reader->reflect(__CLASS__, $method); + $this->reader->reflect($this->controller, $method); $sec->beforeController($this->controller, $method); } - /** - * @PublicPage - */ - public function testCsrfCheck() { - $this->expectException(\OC\AppFramework\Middleware\Security\Exceptions\CrossSiteRequestForgeryException::class); + #[\PHPUnit\Framework\Attributes\DataProvider('dataPublicPage')] + public function testCsrfCheck(string $method): void { + $this->expectException(CrossSiteRequestForgeryException::class); $this->request->expects($this->once()) ->method('passesCSRFCheck') @@ -268,28 +329,22 @@ class SecurityMiddlewareTest extends \Test\TestCase { $this->request->expects($this->once()) ->method('passesStrictCookieCheck') ->willReturn(true); - $this->reader->reflect(__CLASS__, __FUNCTION__); - $this->middleware->beforeController($this->controller, __FUNCTION__); + $this->reader->reflect($this->controller, $method); + $this->middleware->beforeController($this->controller, $method); } - - /** - * @PublicPage - * @NoCSRFRequired - */ - public function testNoCsrfCheck() { + #[\PHPUnit\Framework\Attributes\DataProvider('dataNoCSRFRequiredPublicPage')] + public function testNoCsrfCheck(string $method): void { $this->request->expects($this->never()) ->method('passesCSRFCheck') ->willReturn(false); - $this->reader->reflect(__CLASS__, __FUNCTION__); - $this->middleware->beforeController($this->controller, __FUNCTION__); + $this->reader->reflect($this->controller, $method); + $this->middleware->beforeController($this->controller, $method); } - /** - * @PublicPage - */ - public function testPassesCsrfCheck() { + #[\PHPUnit\Framework\Attributes\DataProvider('dataPublicPage')] + public function testPassesCsrfCheck(string $method): void { $this->request->expects($this->once()) ->method('passesCSRFCheck') ->willReturn(true); @@ -297,15 +352,13 @@ class SecurityMiddlewareTest extends \Test\TestCase { ->method('passesStrictCookieCheck') ->willReturn(true); - $this->reader->reflect(__CLASS__, __FUNCTION__); - $this->middleware->beforeController($this->controller, __FUNCTION__); + $this->reader->reflect($this->controller, $method); + $this->middleware->beforeController($this->controller, $method); } - /** - * @PublicPage - */ - public function testFailCsrfCheck() { - $this->expectException(\OC\AppFramework\Middleware\Security\Exceptions\CrossSiteRequestForgeryException::class); + #[\PHPUnit\Framework\Attributes\DataProvider('dataPublicPage')] + public function testFailCsrfCheck(string $method): void { + $this->expectException(CrossSiteRequestForgeryException::class); $this->request->expects($this->once()) ->method('passesCSRFCheck') @@ -314,16 +367,13 @@ class SecurityMiddlewareTest extends \Test\TestCase { ->method('passesStrictCookieCheck') ->willReturn(true); - $this->reader->reflect(__CLASS__, __FUNCTION__); - $this->middleware->beforeController($this->controller, __FUNCTION__); + $this->reader->reflect($this->controller, $method); + $this->middleware->beforeController($this->controller, $method); } - /** - * @PublicPage - * @StrictCookieRequired - */ - public function testStrictCookieRequiredCheck() { - $this->expectException(\OC\Appframework\Middleware\Security\Exceptions\StrictCookieMissingException::class); + #[\PHPUnit\Framework\Attributes\DataProvider('dataPublicPageStrictCookieRequired')] + public function testStrictCookieRequiredCheck(string $method): void { + $this->expectException(\OC\AppFramework\Middleware\Security\Exceptions\StrictCookieMissingException::class); $this->request->expects($this->never()) ->method('passesCSRFCheck'); @@ -331,68 +381,53 @@ class SecurityMiddlewareTest extends \Test\TestCase { ->method('passesStrictCookieCheck') ->willReturn(false); - $this->reader->reflect(__CLASS__, __FUNCTION__); - $this->middleware->beforeController($this->controller, __FUNCTION__); + $this->reader->reflect($this->controller, $method); + $this->middleware->beforeController($this->controller, $method); } - - /** - * @PublicPage - * @NoCSRFRequired - */ - public function testNoStrictCookieRequiredCheck() { + #[\PHPUnit\Framework\Attributes\DataProvider('dataNoCSRFRequiredPublicPage')] + public function testNoStrictCookieRequiredCheck(string $method): void { $this->request->expects($this->never()) ->method('passesStrictCookieCheck') ->willReturn(false); - $this->reader->reflect(__CLASS__, __FUNCTION__); - $this->middleware->beforeController($this->controller, __FUNCTION__); + $this->reader->reflect($this->controller, $method); + $this->middleware->beforeController($this->controller, $method); } - /** - * @PublicPage - * @NoCSRFRequired - * @StrictCookieRequired - */ - public function testPassesStrictCookieRequiredCheck() { + #[\PHPUnit\Framework\Attributes\DataProvider('dataNoCSRFRequiredPublicPageStrictCookieRequired')] + public function testPassesStrictCookieRequiredCheck(string $method): void { $this->request ->expects($this->once()) ->method('passesStrictCookieCheck') ->willReturn(true); - $this->reader->reflect(__CLASS__, __FUNCTION__); - $this->middleware->beforeController($this->controller, __FUNCTION__); + $this->reader->reflect($this->controller, $method); + $this->middleware->beforeController($this->controller, $method); } - public function dataCsrfOcsController() { - $controller = $this->getMockBuilder('OCP\AppFramework\Controller') - ->disableOriginalConstructor() - ->getMock(); - $ocsController = $this->getMockBuilder('OCP\AppFramework\OCSController') - ->disableOriginalConstructor() - ->getMock(); - + public static function dataCsrfOcsController(): array { return [ - [$controller, false, false, true], - [$controller, false, true, true], - [$controller, true, false, true], - [$controller, true, true, true], - - [$ocsController, false, false, true], - [$ocsController, false, true, false], - [$ocsController, true, false, false], - [$ocsController, true, true, false], + [NormalController::class, false, false, true], + [NormalController::class, false, true, true], + [NormalController::class, true, false, true], + [NormalController::class, true, true, true], + + [OCSController::class, false, false, true], + [OCSController::class, false, true, false], + [OCSController::class, true, false, false], + [OCSController::class, true, true, false], ]; } /** - * @dataProvider dataCsrfOcsController - * @param Controller $controller + * @param string $controllerClass * @param bool $hasOcsApiHeader * @param bool $hasBearerAuth * @param bool $exception */ - public function testCsrfOcsController(Controller $controller, bool $hasOcsApiHeader, bool $hasBearerAuth, bool $exception) { + #[\PHPUnit\Framework\Attributes\DataProvider('dataCsrfOcsController')] + public function testCsrfOcsController(string $controllerClass, bool $hasOcsApiHeader, bool $hasBearerAuth, bool $exception): void { $this->request ->method('getHeader') ->willReturnCallback(function ($header) use ($hasOcsApiHeader, $hasBearerAuth) { @@ -408,6 +443,8 @@ class SecurityMiddlewareTest extends \Test\TestCase { ->method('passesStrictCookieCheck') ->willReturn(true); + $controller = new $controllerClass('test', $this->request); + try { $this->middleware->beforeController($controller, 'foo'); $this->assertFalse($exception); @@ -416,86 +453,112 @@ class SecurityMiddlewareTest extends \Test\TestCase { } } - /** - * @NoCSRFRequired - * @NoAdminRequired - */ - public function testLoggedInCheck() { - $this->securityCheck(__FUNCTION__, 'isLoggedIn'); + #[\PHPUnit\Framework\Attributes\DataProvider('dataNoAdminRequiredNoCSRFRequired')] + public function testLoggedInCheck(string $method): void { + $this->securityCheck($method, 'isLoggedIn'); } - - /** - * @NoCSRFRequired - * @NoAdminRequired - */ - public function testFailLoggedInCheck() { - $this->securityCheck(__FUNCTION__, 'isLoggedIn', true); + #[\PHPUnit\Framework\Attributes\DataProvider('dataNoAdminRequiredNoCSRFRequired')] + public function testFailLoggedInCheck(string $method): void { + $this->securityCheck($method, 'isLoggedIn', true); } - - /** - * @NoCSRFRequired - */ - public function testIsAdminCheck() { - $this->securityCheck(__FUNCTION__, 'isAdminUser'); + #[\PHPUnit\Framework\Attributes\DataProvider('dataNoCSRFRequired')] + public function testIsAdminCheck(string $method): void { + $this->securityCheck($method, 'isAdminUser'); } - /** - * @NoCSRFRequired - * @SubAdminRequired - */ - public function testIsNotSubAdminCheck() { - $this->reader->reflect(__CLASS__, __FUNCTION__); + #[\PHPUnit\Framework\Attributes\DataProvider('dataNoCSRFRequiredSubAdminRequired')] + public function testIsNotSubAdminCheck(string $method): void { + $this->reader->reflect($this->controller, $method); $sec = $this->getMiddleware(true, false, false); $this->expectException(SecurityException::class); - $sec->beforeController($this, __METHOD__); + $sec->beforeController($this->controller, $method); } - /** - * @NoCSRFRequired - * @SubAdminRequired - */ - public function testIsSubAdminCheck() { - $this->reader->reflect(__CLASS__, __FUNCTION__); + #[\PHPUnit\Framework\Attributes\DataProvider('dataNoCSRFRequiredSubAdminRequired')] + public function testIsSubAdminCheck(string $method): void { + $this->reader->reflect($this->controller, $method); $sec = $this->getMiddleware(true, false, true); - $sec->beforeController($this, __METHOD__); + $sec->beforeController($this->controller, $method); $this->addToAssertionCount(1); } - /** - * @NoCSRFRequired - * @SubAdminRequired - */ - public function testIsSubAdminAndAdminCheck() { - $this->reader->reflect(__CLASS__, __FUNCTION__); + #[\PHPUnit\Framework\Attributes\DataProvider('dataNoCSRFRequiredSubAdminRequired')] + public function testIsSubAdminAndAdminCheck(string $method): void { + $this->reader->reflect($this->controller, $method); $sec = $this->getMiddleware(true, true, true); - $sec->beforeController($this, __METHOD__); + $sec->beforeController($this->controller, $method); $this->addToAssertionCount(1); } - /** - * @NoCSRFRequired - */ - public function testFailIsAdminCheck() { - $this->securityCheck(__FUNCTION__, 'isAdminUser', true); + #[\PHPUnit\Framework\Attributes\DataProvider('dataNoCSRFRequired')] + public function testFailIsAdminCheck(string $method): void { + $this->securityCheck($method, 'isAdminUser', true); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('dataNoAdminRequiredNoCSRFRequiredPublicPage')] + public function testRestrictedAppLoggedInPublicPage(string $method): void { + $middleware = $this->getMiddleware(true, false, false); + $this->reader->reflect($this->controller, $method); + + $this->appManager->method('getAppPath') + ->with('files') + ->willReturn('foo'); + + $this->appManager->method('isEnabledForUser') + ->with('files') + ->willReturn(false); + + $middleware->beforeController($this->controller, $method); + $this->addToAssertionCount(1); } + #[\PHPUnit\Framework\Attributes\DataProvider('dataNoAdminRequiredNoCSRFRequiredPublicPage')] + public function testRestrictedAppNotLoggedInPublicPage(string $method): void { + $middleware = $this->getMiddleware(false, false, false); + $this->reader->reflect($this->controller, $method); + + $this->appManager->method('getAppPath') + ->with('files') + ->willReturn('foo'); + + $this->appManager->method('isEnabledForUser') + ->with('files') + ->willReturn(false); + + $middleware->beforeController($this->controller, $method); + $this->addToAssertionCount(1); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('dataNoAdminRequiredNoCSRFRequired')] + public function testRestrictedAppLoggedIn(string $method): void { + $middleware = $this->getMiddleware(true, false, false, false); + $this->reader->reflect($this->controller, $method); + + $this->appManager->method('getAppPath') + ->with('files') + ->willReturn('foo'); + + $this->expectException(AppNotEnabledException::class); + $middleware->beforeController($this->controller, $method); + } - public function testAfterExceptionNotCaughtThrowsItAgain() { + + public function testAfterExceptionNotCaughtThrowsItAgain(): void { $ex = new \Exception(); $this->expectException(\Exception::class); $this->middleware->afterException($this->controller, 'test', $ex); } - public function testAfterExceptionReturnsRedirectForNotLoggedInUser() { + public function testAfterExceptionReturnsRedirectForNotLoggedInUser(): void { $this->request = new Request( [ - 'server' => - [ + 'server' + => [ 'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'REQUEST_URI' => 'nextcloud/index.php/apps/specialapp' ] @@ -526,7 +589,7 @@ class SecurityMiddlewareTest extends \Test\TestCase { $this->assertEquals($expected, $response); } - public function testAfterExceptionRedirectsToWebRootAfterStrictCookieFail() { + public function testAfterExceptionRedirectsToWebRootAfterStrictCookieFail(): void { $this->request = new Request( [ 'server' => [ @@ -553,7 +616,7 @@ class SecurityMiddlewareTest extends \Test\TestCase { /** * @return array */ - public function exceptionProvider() { + public static function exceptionProvider(): array { return [ [ new AppNotEnabledException(), @@ -568,14 +631,14 @@ class SecurityMiddlewareTest extends \Test\TestCase { } /** - * @dataProvider exceptionProvider * @param SecurityException $exception */ - public function testAfterExceptionReturnsTemplateResponse(SecurityException $exception) { + #[\PHPUnit\Framework\Attributes\DataProvider('exceptionProvider')] + public function testAfterExceptionReturnsTemplateResponse(SecurityException $exception): void { $this->request = new Request( [ - 'server' => - [ + 'server' + => [ 'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'REQUEST_URI' => 'nextcloud/index.php/apps/specialapp' ] @@ -597,81 +660,42 @@ class SecurityMiddlewareTest extends \Test\TestCase { $this->assertEquals($expected, $response); } - public function testAfterAjaxExceptionReturnsJSONError() { + public function testAfterAjaxExceptionReturnsJSONError(): void { $response = $this->middleware->afterException($this->controller, 'test', $this->secAjaxException); $this->assertTrue($response instanceof JSONResponse); } - public function dataRestrictedApp() { - return [ - [false, false, false,], - [false, false, true,], - [false, true, false,], - [false, true, true,], - [ true, false, false,], - [ true, false, true,], - [ true, true, false,], - [ true, true, true,], - ]; - } - - /** - * @PublicPage - * @NoAdminRequired - * @NoCSRFRequired - */ - public function testRestrictedAppLoggedInPublicPage() { + #[\PHPUnit\Framework\Attributes\DataProvider('dataExAppRequired')] + public function testExAppRequired(string $method): void { $middleware = $this->getMiddleware(true, false, false); - $this->reader->reflect(__CLASS__, __FUNCTION__); - - $this->appManager->method('getAppPath') - ->with('files') - ->willReturn('foo'); + $this->reader->reflect($this->controller, $method); - $this->appManager->method('isEnabledForUser') - ->with('files') - ->willReturn(false); - - $middleware->beforeController($this->controller, __FUNCTION__); - $this->addToAssertionCount(1); - } - - /** - * @PublicPage - * @NoAdminRequired - * @NoCSRFRequired - */ - public function testRestrictedAppNotLoggedInPublicPage() { - $middleware = $this->getMiddleware(false, false, false); - $this->reader->reflect(__CLASS__, __FUNCTION__); + $session = $this->createMock(ISession::class); + $session->method('get')->with('app_api')->willReturn(true); + $this->userSession->method('getSession')->willReturn($session); - $this->appManager->method('getAppPath') - ->with('files') - ->willReturn('foo'); - - $this->appManager->method('isEnabledForUser') - ->with('files') - ->willReturn(false); + $this->request->expects($this->once()) + ->method('passesStrictCookieCheck') + ->willReturn(true); + $this->request->expects($this->once()) + ->method('passesCSRFCheck') + ->willReturn(true); - $middleware->beforeController($this->controller, __FUNCTION__); - $this->addToAssertionCount(1); + $middleware->beforeController($this->controller, $method); } - /** - * @NoAdminRequired - * @NoCSRFRequired - */ - public function testRestrictedAppLoggedIn() { + #[\PHPUnit\Framework\Attributes\DataProvider('dataExAppRequired')] + public function testExAppRequiredError(string $method): void { $middleware = $this->getMiddleware(true, false, false, false); - $this->reader->reflect(__CLASS__, __FUNCTION__); + $this->reader->reflect($this->controller, $method); - $this->appManager->method('getAppPath') - ->with('files') - ->willReturn('foo'); + $session = $this->createMock(ISession::class); + $session->method('get')->with('app_api')->willReturn(false); + $this->userSession->method('getSession')->willReturn($session); - $this->expectException(AppNotEnabledException::class); - $middleware->beforeController($this->controller, __FUNCTION__); + $this->expectException(ExAppRequiredException::class); + $middleware->beforeController($this->controller, $method); } } diff --git a/tests/lib/AppFramework/Middleware/SessionMiddlewareTest.php b/tests/lib/AppFramework/Middleware/SessionMiddlewareTest.php index f9739044465..8ecc73791c9 100644 --- a/tests/lib/AppFramework/Middleware/SessionMiddlewareTest.php +++ b/tests/lib/AppFramework/Middleware/SessionMiddlewareTest.php @@ -1,84 +1,138 @@ <?php + +declare(strict_types=1); /** - * ownCloud - App Framework - * - * This file is licensed under the Affero General Public License version 3 or - * later. See the COPYING file. - * - * @author Thomas Müller <deepdiver@owncloud.com> - * @copyright Thomas Müller 2014 + * SPDX-FileCopyrightText: 2016-2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace Test\AppFramework\Middleware; use OC\AppFramework\Middleware\SessionMiddleware; use OC\AppFramework\Utility\ControllerMethodReflector; use OCP\AppFramework\Controller; +use OCP\AppFramework\Http\Attribute\UseSession; use OCP\AppFramework\Http\Response; +use OCP\IRequest; +use OCP\ISession; +use PHPUnit\Framework\MockObject\MockObject; +use Test\TestCase; -class SessionMiddlewareTest extends \Test\TestCase { - - /** @var ControllerMethodReflector */ - private $reflector; - - /** @var Controller */ - private $controller; +class SessionMiddlewareTest extends TestCase { + private ControllerMethodReflector|MockObject $reflector; + private ISession|MockObject $session; + private Controller $controller; + private SessionMiddleware $middleware; protected function setUp(): void { parent::setUp(); - $this->reflector = new ControllerMethodReflector(); - $this->controller = $this->createMock(Controller::class); + $this->reflector = $this->createMock(ControllerMethodReflector::class); + $this->session = $this->createMock(ISession::class); + $this->controller = new class('app', $this->createMock(IRequest::class)) extends Controller { + /** + * @UseSession + */ + public function withAnnotation() { + } + #[UseSession] + public function withAttribute() { + } + public function without() { + } + }; + $this->middleware = new SessionMiddleware( + $this->reflector, + $this->session, + ); } - /** - * @UseSession - */ - public function testSessionNotClosedOnBeforeController() { - $session = $this->getSessionMock(0); + public function testSessionNotClosedOnBeforeController(): void { + $this->configureSessionMock(0, 1); + $this->reflector->expects(self::once()) + ->method('hasAnnotation') + ->with('UseSession') + ->willReturn(true); - $this->reflector->reflect($this, __FUNCTION__); - $middleware = new SessionMiddleware($this->reflector, $session); - $middleware->beforeController($this->controller, __FUNCTION__); + $this->middleware->beforeController($this->controller, 'withAnnotation'); } - /** - * @UseSession - */ - public function testSessionClosedOnAfterController() { - $session = $this->getSessionMock(1); + public function testSessionNotClosedOnBeforeControllerWithAttribute(): void { + $this->configureSessionMock(0, 1); + $this->reflector->expects(self::once()) + ->method('hasAnnotation') + ->with('UseSession') + ->willReturn(false); - $this->reflector->reflect($this, __FUNCTION__); - $middleware = new SessionMiddleware($this->reflector, $session); - $middleware->afterController($this->controller, __FUNCTION__, new Response()); + $this->middleware->beforeController($this->controller, 'withAttribute'); } - public function testSessionClosedOnBeforeController() { - $session = $this->getSessionMock(1); + public function testSessionClosedOnAfterController(): void { + $this->configureSessionMock(1); + $this->reflector->expects(self::once()) + ->method('hasAnnotation') + ->with('UseSession') + ->willReturn(true); - $this->reflector->reflect($this, __FUNCTION__); - $middleware = new SessionMiddleware($this->reflector, $session); - $middleware->beforeController($this->controller, __FUNCTION__); + $this->middleware->afterController($this->controller, 'withAnnotation', new Response()); } - public function testSessionNotClosedOnAfterController() { - $session = $this->getSessionMock(0); + public function testSessionClosedOnAfterControllerWithAttribute(): void { + $this->configureSessionMock(1); + $this->reflector->expects(self::once()) + ->method('hasAnnotation') + ->with('UseSession') + ->willReturn(true); - $this->reflector->reflect($this, __FUNCTION__); - $middleware = new SessionMiddleware($this->reflector, $session); - $middleware->afterController($this->controller, __FUNCTION__, new Response()); + $this->middleware->afterController($this->controller, 'withAttribute', new Response()); } - /** - * @return mixed - */ - private function getSessionMock($expectedCloseCount) { - $session = $this->getMockBuilder('\OC\Session\Memory') - ->disableOriginalConstructor() - ->getMock(); + public function testSessionReopenedAndClosedOnBeforeController(): void { + $this->configureSessionMock(1, 1); + $this->reflector->expects(self::exactly(2)) + ->method('hasAnnotation') + ->with('UseSession') + ->willReturn(true); + + $this->middleware->beforeController($this->controller, 'withAnnotation'); + $this->middleware->afterController($this->controller, 'withAnnotation', new Response()); + } + + public function testSessionReopenedAndClosedOnBeforeControllerWithAttribute(): void { + $this->configureSessionMock(1, 1); + $this->reflector->expects(self::exactly(2)) + ->method('hasAnnotation') + ->with('UseSession') + ->willReturn(false); + + $this->middleware->beforeController($this->controller, 'withAttribute'); + $this->middleware->afterController($this->controller, 'withAttribute', new Response()); + } + + public function testSessionClosedOnBeforeController(): void { + $this->configureSessionMock(0); + $this->reflector->expects(self::once()) + ->method('hasAnnotation') + ->with('UseSession') + ->willReturn(false); + + $this->middleware->beforeController($this->controller, 'without'); + } + + public function testSessionNotClosedOnAfterController(): void { + $this->configureSessionMock(0); + $this->reflector->expects(self::once()) + ->method('hasAnnotation') + ->with('UseSession') + ->willReturn(false); + + $this->middleware->afterController($this->controller, 'without', new Response()); + } - $session->expects($this->exactly($expectedCloseCount)) + private function configureSessionMock(int $expectedCloseCount, int $expectedReopenCount = 0): void { + $this->session->expects($this->exactly($expectedCloseCount)) ->method('close'); - return $session; + $this->session->expects($this->exactly($expectedReopenCount)) + ->method('reopen'); } } diff --git a/tests/lib/AppFramework/OCS/BaseResponseTest.php b/tests/lib/AppFramework/OCS/BaseResponseTest.php index aca8c355f41..e04f7856623 100644 --- a/tests/lib/AppFramework/OCS/BaseResponseTest.php +++ b/tests/lib/AppFramework/OCS/BaseResponseTest.php @@ -3,34 +3,27 @@ declare(strict_types=1); /** - * @copyright 2020 Daniel Kesselberg <mail@danielkesselberg.de> - * - * @author 2020 Daniel Kesselberg <mail@danielkesselberg.de> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ -namespace Test\AppFramework\Middleware; +namespace Test\AppFramework\OCS; use OC\AppFramework\OCS\BaseResponse; +class ArrayValue implements \JsonSerializable { + public function __construct( + private array $array, + ) { + } + + public function jsonSerialize(): mixed { + return $this->array; + } +} + class BaseResponseTest extends \Test\TestCase { public function testToXml(): void { - /** @var BaseResponse $response */ $response = $this->createMock(BaseResponse::class); @@ -46,13 +39,42 @@ class BaseResponseTest extends \Test\TestCase { 'someElement' => 'withAttribute', ], 'value without key', + 'object' => new \stdClass(), + ]; + + $this->invokePrivate($response, 'toXml', [$data, $writer]); + $writer->endDocument(); + + $this->assertEquals( + "<?xml version=\"1.0\"?>\n<hello>hello</hello><information test=\"some data\"><someElement>withAttribute</someElement></information><element>value without key</element><object/>\n", + $writer->outputMemory(true) + ); + } + + public function testToXmlJsonSerializable(): void { + /** @var BaseResponse $response */ + $response = $this->createMock(BaseResponse::class); + + $writer = new \XMLWriter(); + $writer->openMemory(); + $writer->setIndent(false); + $writer->startDocument(); + + $data = [ + 'hello' => 'hello', + 'information' => new ArrayValue([ + '@test' => 'some data', + 'someElement' => 'withAttribute', + ]), + 'value without key', + 'object' => new \stdClass(), ]; $this->invokePrivate($response, 'toXml', [$data, $writer]); $writer->endDocument(); $this->assertEquals( - "<?xml version=\"1.0\"?>\n<hello>hello</hello><information test=\"some data\"><someElement>withAttribute</someElement></information><element>value without key</element>\n", + "<?xml version=\"1.0\"?>\n<hello>hello</hello><information test=\"some data\"><someElement>withAttribute</someElement></information><element>value without key</element><object/>\n", $writer->outputMemory(true) ); } diff --git a/tests/lib/AppFramework/OCS/V2ResponseTest.php b/tests/lib/AppFramework/OCS/V2ResponseTest.php new file mode 100644 index 00000000000..7a70ad6d633 --- /dev/null +++ b/tests/lib/AppFramework/OCS/V2ResponseTest.php @@ -0,0 +1,36 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace Test\AppFramework\OCS; + +use OC\AppFramework\OCS\V2Response; +use OCP\AppFramework\Http; +use OCP\AppFramework\Http\DataResponse; +use OCP\AppFramework\OCSController; + +class V2ResponseTest extends \Test\TestCase { + #[\PHPUnit\Framework\Attributes\DataProvider('providesStatusCodes')] + public function testStatusCodeMapper(int $expected, int $sc): void { + $response = new V2Response(new DataResponse([], $sc)); + $this->assertEquals($expected, $response->getStatus()); + } + + public static function providesStatusCodes(): array { + return [ + [Http::STATUS_OK, 200], + [Http::STATUS_BAD_REQUEST, 104], + [Http::STATUS_BAD_REQUEST, 1000], + [201, 201], + [Http::STATUS_UNAUTHORIZED, OCSController::RESPOND_UNAUTHORISED], + [Http::STATUS_INTERNAL_SERVER_ERROR, OCSController::RESPOND_SERVER_ERROR], + [Http::STATUS_NOT_FOUND, OCSController::RESPOND_NOT_FOUND], + [Http::STATUS_INTERNAL_SERVER_ERROR, OCSController::RESPOND_UNKNOWN_ERROR], + ]; + } +} diff --git a/tests/lib/AppFramework/Routing/RouteParserTest.php b/tests/lib/AppFramework/Routing/RouteParserTest.php new file mode 100644 index 00000000000..406c5f1f3a5 --- /dev/null +++ b/tests/lib/AppFramework/Routing/RouteParserTest.php @@ -0,0 +1,347 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace Test\AppFramework\Routing; + +use OC\AppFramework\Routing\RouteParser; +use Symfony\Component\Routing\Route as RoutingRoute; +use Symfony\Component\Routing\RouteCollection; + +class RouteParserTest extends \Test\TestCase { + + protected RouteParser $parser; + + protected function setUp(): void { + $this->parser = new RouteParser(); + } + + public function testParseRoutes(): void { + $routes = ['routes' => [ + ['name' => 'folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET'], + ['name' => 'folders#create', 'url' => '/{folderId}/create', 'verb' => 'POST'] + ]]; + + $collection = $this->parser->parseDefaultRoutes($routes, 'app1'); + $this->assertArrayHasKey('app1.folders.open', $collection->all()); + $this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'FoldersController', 'open', route: $collection->get('app1.folders.open')); + $this->assertArrayHasKey('app1.folders.create', $collection->all()); + $this->assertSimpleRoute('/apps/app1/{folderId}/create', 'POST', 'FoldersController', 'create', route: $collection->get('app1.folders.create')); + } + + public function testParseRoutesRootApps(): void { + $routes = ['routes' => [ + ['name' => 'folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET'], + ['name' => 'folders#create', 'url' => '/{folderId}/create', 'verb' => 'POST'] + ]]; + + $collection = $this->parser->parseDefaultRoutes($routes, 'core'); + $this->assertArrayHasKey('core.folders.open', $collection->all()); + $this->assertSimpleRoute('/{folderId}/open', 'GET', 'FoldersController', 'open', app: 'core', route: $collection->get('core.folders.open')); + $this->assertArrayHasKey('core.folders.create', $collection->all()); + $this->assertSimpleRoute('/{folderId}/create', 'POST', 'FoldersController', 'create', app: 'core', route: $collection->get('core.folders.create')); + } + + public function testParseRoutesWithResources(): void { + $routes = ['routes' => [ + ['name' => 'folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET'], + ], 'resources' => [ + 'names' => ['url' => '/names'], + 'folder_names' => ['url' => '/folder/names'], + ]]; + + $collection = $this->parser->parseDefaultRoutes($routes, 'app1'); + $this->assertArrayHasKey('app1.folders.open', $collection->all()); + $this->assertSimpleResource('/apps/app1/folder/names', 'folder_names', 'FolderNamesController', 'app1', $collection); + $this->assertSimpleResource('/apps/app1/names', 'names', 'NamesController', 'app1', $collection); + } + + public function testParseRoutesWithPostfix(): void { + $routes = ['routes' => [ + ['name' => 'folders#update', 'url' => '/{folderId}/update', 'verb' => 'POST'], + ['name' => 'folders#update', 'url' => '/{folderId}/update', 'verb' => 'PUT', 'postfix' => '-edit'] + ]]; + + $collection = $this->parser->parseDefaultRoutes($routes, 'app1'); + $this->assertArrayHasKey('app1.folders.update', $collection->all()); + $this->assertSimpleRoute('/apps/app1/{folderId}/update', 'POST', 'FoldersController', 'update', route: $collection->get('app1.folders.update')); + $this->assertArrayHasKey('app1.folders.update-edit', $collection->all()); + $this->assertSimpleRoute('/apps/app1/{folderId}/update', 'PUT', 'FoldersController', 'update', route: $collection->get('app1.folders.update-edit')); + } + + public function testParseRoutesKebabCaseAction(): void { + $routes = ['routes' => [ + ['name' => 'folders#open_folder', 'url' => '/{folderId}/open', 'verb' => 'GET'] + ]]; + + $collection = $this->parser->parseDefaultRoutes($routes, 'app1'); + $this->assertArrayHasKey('app1.folders.open_folder', $collection->all()); + $this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'FoldersController', 'openFolder', route: $collection->get('app1.folders.open_folder')); + } + + public function testParseRoutesKebabCaseController(): void { + $routes = ['routes' => [ + ['name' => 'my_folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET'] + ]]; + + $collection = $this->parser->parseDefaultRoutes($routes, 'app1'); + $this->assertArrayHasKey('app1.my_folders.open', $collection->all()); + $this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'MyFoldersController', 'open', route: $collection->get('app1.my_folders.open')); + } + + public function testParseRoutesLowercaseVerb(): void { + $routes = ['routes' => [ + ['name' => 'folders#delete', 'url' => '/{folderId}/delete', 'verb' => 'delete'] + ]]; + + $collection = $this->parser->parseDefaultRoutes($routes, 'app1'); + $this->assertArrayHasKey('app1.folders.delete', $collection->all()); + $this->assertSimpleRoute('/apps/app1/{folderId}/delete', 'DELETE', 'FoldersController', 'delete', route: $collection->get('app1.folders.delete')); + } + + public function testParseRoutesMissingVerb(): void { + $routes = ['routes' => [ + ['name' => 'folders#open', 'url' => '/{folderId}/open'] + ]]; + + $collection = $this->parser->parseDefaultRoutes($routes, 'app1'); + $this->assertArrayHasKey('app1.folders.open', $collection->all()); + $this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'FoldersController', 'open', route: $collection->get('app1.folders.open')); + } + + public function testParseRoutesWithRequirements(): void { + $routes = ['routes' => [ + ['name' => 'folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET', 'requirements' => ['folderId' => '\d+']] + ]]; + + $collection = $this->parser->parseDefaultRoutes($routes, 'app1'); + $this->assertArrayHasKey('app1.folders.open', $collection->all()); + $this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'FoldersController', 'open', requirements: ['folderId' => '\d+'], route: $collection->get('app1.folders.open')); + } + + public function testParseRoutesWithDefaults(): void { + $routes = ['routes' => [ + ['name' => 'folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET', 'defaults' => ['hello' => 'world']] + ]]; + + $collection = $this->parser->parseDefaultRoutes($routes, 'app1'); + $this->assertArrayHasKey('app1.folders.open', $collection->all()); + $this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'FoldersController', 'open', defaults: ['hello' => 'world'], route: $collection->get('app1.folders.open')); + } + + public function testParseRoutesInvalidName(): void { + $routes = ['routes' => [ + ['name' => 'folders', 'url' => '/{folderId}/open', 'verb' => 'GET'] + ]]; + + $this->expectException(\UnexpectedValueException::class); + $this->parser->parseDefaultRoutes($routes, 'app1'); + } + + public function testParseRoutesInvalidName2(): void { + $routes = ['routes' => [ + ['name' => 'folders#open#action', 'url' => '/{folderId}/open', 'verb' => 'GET'] + ]]; + + $this->expectException(\UnexpectedValueException::class); + $this->parser->parseDefaultRoutes($routes, 'app1'); + } + + public function testParseRoutesEmpty(): void { + $routes = ['routes' => []]; + + $collection = $this->parser->parseDefaultRoutes($routes, 'app1'); + $this->assertEquals(0, $collection->count()); + } + + // OCS routes + + public function testParseOcsRoutes(): void { + $routes = ['ocs' => [ + ['name' => 'folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET'], + ['name' => 'folders#create', 'url' => '/{folderId}/create', 'verb' => 'POST'] + ]]; + + $collection = $this->parser->parseOCSRoutes($routes, 'app1'); + $this->assertArrayHasKey('ocs.app1.folders.open', $collection->all()); + $this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'FoldersController', 'open', route: $collection->get('ocs.app1.folders.open')); + $this->assertArrayHasKey('ocs.app1.folders.create', $collection->all()); + $this->assertSimpleRoute('/apps/app1/{folderId}/create', 'POST', 'FoldersController', 'create', route: $collection->get('ocs.app1.folders.create')); + } + + public function testParseOcsRoutesRootApps(): void { + $routes = ['ocs' => [ + ['name' => 'folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET'], + ['name' => 'folders#create', 'url' => '/{folderId}/create', 'verb' => 'POST'] + ]]; + + $collection = $this->parser->parseOCSRoutes($routes, 'core'); + $this->assertArrayHasKey('ocs.core.folders.open', $collection->all()); + $this->assertSimpleRoute('/{folderId}/open', 'GET', 'FoldersController', 'open', app: 'core', route: $collection->get('ocs.core.folders.open')); + $this->assertArrayHasKey('ocs.core.folders.create', $collection->all()); + $this->assertSimpleRoute('/{folderId}/create', 'POST', 'FoldersController', 'create', app: 'core', route: $collection->get('ocs.core.folders.create')); + } + + public function testParseOcsRoutesWithPostfix(): void { + $routes = ['ocs' => [ + ['name' => 'folders#update', 'url' => '/{folderId}/update', 'verb' => 'POST'], + ['name' => 'folders#update', 'url' => '/{folderId}/update', 'verb' => 'PUT', 'postfix' => '-edit'] + ]]; + + $collection = $this->parser->parseOCSRoutes($routes, 'app1'); + $this->assertArrayHasKey('ocs.app1.folders.update', $collection->all()); + $this->assertSimpleRoute('/apps/app1/{folderId}/update', 'POST', 'FoldersController', 'update', route: $collection->get('ocs.app1.folders.update')); + $this->assertArrayHasKey('ocs.app1.folders.update-edit', $collection->all()); + $this->assertSimpleRoute('/apps/app1/{folderId}/update', 'PUT', 'FoldersController', 'update', route: $collection->get('ocs.app1.folders.update-edit')); + } + + public function testParseOcsRoutesKebabCaseAction(): void { + $routes = ['ocs' => [ + ['name' => 'folders#open_folder', 'url' => '/{folderId}/open', 'verb' => 'GET'] + ]]; + + $collection = $this->parser->parseOCSRoutes($routes, 'app1'); + $this->assertArrayHasKey('ocs.app1.folders.open_folder', $collection->all()); + $this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'FoldersController', 'openFolder', route: $collection->get('ocs.app1.folders.open_folder')); + } + + public function testParseOcsRoutesKebabCaseController(): void { + $routes = ['ocs' => [ + ['name' => 'my_folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET'] + ]]; + + $collection = $this->parser->parseOCSRoutes($routes, 'app1'); + $this->assertArrayHasKey('ocs.app1.my_folders.open', $collection->all()); + $this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'MyFoldersController', 'open', route: $collection->get('ocs.app1.my_folders.open')); + } + + public function testParseOcsRoutesLowercaseVerb(): void { + $routes = ['ocs' => [ + ['name' => 'folders#delete', 'url' => '/{folderId}/delete', 'verb' => 'delete'] + ]]; + + $collection = $this->parser->parseOCSRoutes($routes, 'app1'); + $this->assertArrayHasKey('ocs.app1.folders.delete', $collection->all()); + $this->assertSimpleRoute('/apps/app1/{folderId}/delete', 'DELETE', 'FoldersController', 'delete', route: $collection->get('ocs.app1.folders.delete')); + } + + public function testParseOcsRoutesMissingVerb(): void { + $routes = ['ocs' => [ + ['name' => 'folders#open', 'url' => '/{folderId}/open'] + ]]; + + $collection = $this->parser->parseOCSRoutes($routes, 'app1'); + $this->assertArrayHasKey('ocs.app1.folders.open', $collection->all()); + $this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'FoldersController', 'open', route: $collection->get('ocs.app1.folders.open')); + } + + public function testParseOcsRoutesWithRequirements(): void { + $routes = ['ocs' => [ + ['name' => 'folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET', 'requirements' => ['folderId' => '\d+']] + ]]; + + $collection = $this->parser->parseOCSRoutes($routes, 'app1'); + $this->assertArrayHasKey('ocs.app1.folders.open', $collection->all()); + $this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'FoldersController', 'open', requirements: ['folderId' => '\d+'], route: $collection->get('ocs.app1.folders.open')); + } + + public function testParseOcsRoutesWithDefaults(): void { + $routes = ['ocs' => [ + ['name' => 'folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET', 'defaults' => ['hello' => 'world']] + ]]; + + $collection = $this->parser->parseOCSRoutes($routes, 'app1'); + $this->assertArrayHasKey('ocs.app1.folders.open', $collection->all()); + $this->assertSimpleRoute('/apps/app1/{folderId}/open', 'GET', 'FoldersController', 'open', defaults: ['hello' => 'world'], route: $collection->get('ocs.app1.folders.open')); + } + + public function testParseOcsRoutesInvalidName(): void { + $routes = ['ocs' => [ + ['name' => 'folders', 'url' => '/{folderId}/open', 'verb' => 'GET'] + ]]; + + $this->expectException(\UnexpectedValueException::class); + $this->parser->parseOCSRoutes($routes, 'app1'); + } + + public function testParseOcsRoutesEmpty(): void { + $routes = ['ocs' => []]; + + $collection = $this->parser->parseOCSRoutes($routes, 'app1'); + $this->assertEquals(0, $collection->count()); + } + + public function testParseOcsRoutesWithResources(): void { + $routes = ['ocs' => [ + ['name' => 'folders#open', 'url' => '/{folderId}/open', 'verb' => 'GET'], + ], 'ocs-resources' => [ + 'names' => ['url' => '/names', 'root' => '/core/something'], + 'folder_names' => ['url' => '/folder/names'], + ]]; + + $collection = $this->parser->parseOCSRoutes($routes, 'app1'); + $this->assertArrayHasKey('ocs.app1.folders.open', $collection->all()); + $this->assertOcsResource('/apps/app1/folder/names', 'folder_names', 'FolderNamesController', 'app1', $collection); + $this->assertOcsResource('/core/something/names', 'names', 'NamesController', 'app1', $collection); + } + + protected function assertSimpleRoute( + string $path, + string $method, + string $controller, + string $action, + string $app = 'app1', + array $requirements = [], + array $defaults = [], + ?RoutingRoute $route = null, + ): void { + self::assertEquals($path, $route->getPath()); + self::assertEqualsCanonicalizing([$method], $route->getMethods()); + self::assertEqualsCanonicalizing($requirements, $route->getRequirements()); + self::assertEquals([...$defaults, 'action' => null, 'caller' => [$app, $controller, $action]], $route->getDefaults()); + } + + protected function assertSimpleResource( + string $path, + string $resourceName, + string $controller, + string $app, + RouteCollection $collection, + ): void { + self::assertArrayHasKey("$app.$resourceName.index", $collection->all()); + self::assertArrayHasKey("$app.$resourceName.show", $collection->all()); + self::assertArrayHasKey("$app.$resourceName.create", $collection->all()); + self::assertArrayHasKey("$app.$resourceName.update", $collection->all()); + self::assertArrayHasKey("$app.$resourceName.destroy", $collection->all()); + + $this->assertSimpleRoute($path, 'GET', $controller, 'index', $app, route: $collection->get("$app.$resourceName.index")); + $this->assertSimpleRoute($path, 'POST', $controller, 'create', $app, route: $collection->get("$app.$resourceName.create")); + $this->assertSimpleRoute("$path/{id}", 'GET', $controller, 'show', $app, route: $collection->get("$app.$resourceName.show")); + $this->assertSimpleRoute("$path/{id}", 'PUT', $controller, 'update', $app, route: $collection->get("$app.$resourceName.update")); + $this->assertSimpleRoute("$path/{id}", 'DELETE', $controller, 'destroy', $app, route: $collection->get("$app.$resourceName.destroy")); + } + + protected function assertOcsResource( + string $path, + string $resourceName, + string $controller, + string $app, + RouteCollection $collection, + ): void { + self::assertArrayHasKey("ocs.$app.$resourceName.index", $collection->all()); + self::assertArrayHasKey("ocs.$app.$resourceName.show", $collection->all()); + self::assertArrayHasKey("ocs.$app.$resourceName.create", $collection->all()); + self::assertArrayHasKey("ocs.$app.$resourceName.update", $collection->all()); + self::assertArrayHasKey("ocs.$app.$resourceName.destroy", $collection->all()); + + $this->assertSimpleRoute($path, 'GET', $controller, 'index', $app, route: $collection->get("ocs.$app.$resourceName.index")); + $this->assertSimpleRoute($path, 'POST', $controller, 'create', $app, route: $collection->get("ocs.$app.$resourceName.create")); + $this->assertSimpleRoute("$path/{id}", 'GET', $controller, 'show', $app, route: $collection->get("ocs.$app.$resourceName.show")); + $this->assertSimpleRoute("$path/{id}", 'PUT', $controller, 'update', $app, route: $collection->get("ocs.$app.$resourceName.update")); + $this->assertSimpleRoute("$path/{id}", 'DELETE', $controller, 'destroy', $app, route: $collection->get("ocs.$app.$resourceName.destroy")); + } +} diff --git a/tests/lib/AppFramework/Routing/RoutingTest.php b/tests/lib/AppFramework/Routing/RoutingTest.php deleted file mode 100644 index b4965d61d4f..00000000000 --- a/tests/lib/AppFramework/Routing/RoutingTest.php +++ /dev/null @@ -1,451 +0,0 @@ -<?php - -namespace Test\AppFramework\Routing; - -use OC\AppFramework\DependencyInjection\DIContainer; -use OC\AppFramework\Routing\RouteConfig; -use OC\Route\Route; -use OC\Route\Router; -use OCP\Route\IRouter; -use PHPUnit\Framework\MockObject\MockObject; -use Psr\Log\LoggerInterface; - -class RoutingTest extends \Test\TestCase { - public function testSimpleRoute() { - $routes = ['routes' => [ - ['name' => 'folders#open', 'url' => '/folders/{folderId}/open', 'verb' => 'GET'] - ]]; - - $this->assertSimpleRoute($routes, 'folders.open', 'GET', '/apps/app1/folders/{folderId}/open', 'FoldersController', 'open'); - } - - public function testSimpleRouteWithUnderScoreNames() { - $routes = ['routes' => [ - ['name' => 'admin_folders#open_current', 'url' => '/folders/{folderId}/open', 'verb' => 'delete', 'root' => ''] - ]]; - - $this->assertSimpleRoute($routes, 'admin_folders.open_current', 'DELETE', '/folders/{folderId}/open', 'AdminFoldersController', 'openCurrent', [], [], '', true); - } - - public function testSimpleOCSRoute() { - $routes = ['ocs' => [ - ['name' => 'folders#open', 'url' => '/folders/{folderId}/open', 'verb' => 'GET'] - ] - ]; - - $this->assertSimpleOCSRoute($routes, 'folders.open', 'GET', '/apps/app1/folders/{folderId}/open', 'FoldersController', 'open'); - } - - public function testSimpleRouteWithMissingVerb() { - $routes = ['routes' => [ - ['name' => 'folders#open', 'url' => '/folders/{folderId}/open'] - ]]; - - $this->assertSimpleRoute($routes, 'folders.open', 'GET', '/apps/app1/folders/{folderId}/open', 'FoldersController', 'open'); - } - - public function testSimpleOCSRouteWithMissingVerb() { - $routes = ['ocs' => [ - ['name' => 'folders#open', 'url' => '/folders/{folderId}/open'] - ] - ]; - - $this->assertSimpleOCSRoute($routes, 'folders.open', 'GET', '/apps/app1/folders/{folderId}/open', 'FoldersController', 'open'); - } - - public function testSimpleRouteWithLowercaseVerb() { - $routes = ['routes' => [ - ['name' => 'folders#open', 'url' => '/folders/{folderId}/open', 'verb' => 'delete'] - ]]; - - $this->assertSimpleRoute($routes, 'folders.open', 'DELETE', '/apps/app1/folders/{folderId}/open', 'FoldersController', 'open'); - } - - public function testSimpleOCSRouteWithLowercaseVerb() { - $routes = ['ocs' => [ - ['name' => 'folders#open', 'url' => '/folders/{folderId}/open', 'verb' => 'delete'] - ] - ]; - - $this->assertSimpleOCSRoute($routes, 'folders.open', 'DELETE', '/apps/app1/folders/{folderId}/open', 'FoldersController', 'open'); - } - - public function testSimpleRouteWithRequirements() { - $routes = ['routes' => [ - ['name' => 'folders#open', 'url' => '/folders/{folderId}/open', 'verb' => 'delete', 'requirements' => ['something']] - ]]; - - $this->assertSimpleRoute($routes, 'folders.open', 'DELETE', '/apps/app1/folders/{folderId}/open', 'FoldersController', 'open', ['something']); - } - - public function testSimpleOCSRouteWithRequirements() { - $routes = ['ocs' => [ - ['name' => 'folders#open', 'url' => '/folders/{folderId}/open', 'verb' => 'delete', 'requirements' => ['something']] - ] - ]; - - $this->assertSimpleOCSRoute($routes, 'folders.open', 'DELETE', '/apps/app1/folders/{folderId}/open', 'FoldersController', 'open', ['something']); - } - - public function testSimpleRouteWithDefaults() { - $routes = ['routes' => [ - ['name' => 'folders#open', 'url' => '/folders/{folderId}/open', 'verb' => 'delete', [], 'defaults' => ['param' => 'foobar']] - ]]; - - $this->assertSimpleRoute($routes, 'folders.open', 'DELETE', '/apps/app1/folders/{folderId}/open', 'FoldersController', 'open', [], ['param' => 'foobar']); - } - - - public function testSimpleOCSRouteWithDefaults() { - $routes = ['ocs' => [ - ['name' => 'folders#open', 'url' => '/folders/{folderId}/open', 'verb' => 'delete', 'defaults' => ['param' => 'foobar']] - ] - ]; - - $this->assertSimpleOCSRoute($routes, 'folders.open', 'DELETE', '/apps/app1/folders/{folderId}/open', 'FoldersController', 'open', [], ['param' => 'foobar']); - } - - public function testSimpleRouteWithPostfix() { - $routes = ['routes' => [ - ['name' => 'folders#open', 'url' => '/folders/{folderId}/open', 'verb' => 'delete', 'postfix' => '_something'] - ]]; - - $this->assertSimpleRoute($routes, 'folders.open', 'DELETE', '/apps/app1/folders/{folderId}/open', 'FoldersController', 'open', [], [], '_something'); - } - - public function testSimpleOCSRouteWithPostfix() { - $routes = ['ocs' => [ - ['name' => 'folders#open', 'url' => '/folders/{folderId}/open', 'verb' => 'delete', 'postfix' => '_something'] - ] - ]; - - $this->assertSimpleOCSRoute($routes, 'folders.open', 'DELETE', '/apps/app1/folders/{folderId}/open', 'FoldersController', 'open', [], [], '_something'); - } - - - public function testSimpleRouteWithBrokenName() { - $this->expectException(\UnexpectedValueException::class); - - $routes = ['routes' => [ - ['name' => 'folders_open', 'url' => '/folders/{folderId}/open', 'verb' => 'delete'] - ]]; - - /** @var IRouter|MockObject $router */ - $router = $this->getMockBuilder(Router::class) - ->onlyMethods(['create']) - ->setConstructorArgs([$this->createMock(LoggerInterface::class)]) - ->getMock(); - - // load route configuration - $container = new DIContainer('app1'); - $config = new RouteConfig($container, $router, $routes); - - $config->register(); - } - - - public function testSimpleOCSRouteWithBrokenName() { - $this->expectException(\UnexpectedValueException::class); - - $routes = ['ocs' => [ - ['name' => 'folders_open', 'url' => '/folders/{folderId}/open', 'verb' => 'delete'] - ]]; - - /** @var IRouter|MockObject $router */ - $router = $this->getMockBuilder(Router::class) - ->onlyMethods(['create']) - ->setConstructorArgs([$this->createMock(LoggerInterface::class)]) - ->getMock(); - - // load route configuration - $container = new DIContainer('app1'); - $config = new RouteConfig($container, $router, $routes); - - $config->register(); - } - - public function testSimpleOCSRouteWithUnderScoreNames() { - $routes = ['ocs' => [ - ['name' => 'admin_folders#open_current', 'url' => '/folders/{folderId}/open', 'verb' => 'delete'] - ]]; - - $this->assertSimpleOCSRoute($routes, 'admin_folders.open_current', 'DELETE', '/apps/app1/folders/{folderId}/open', 'AdminFoldersController', 'openCurrent'); - } - - public function testOCSResource() { - $routes = ['ocs-resources' => ['account' => ['url' => '/accounts']]]; - - $this->assertOCSResource($routes, 'account', '/apps/app1/accounts', 'AccountController', 'id'); - } - - public function testOCSResourceWithUnderScoreName() { - $routes = ['ocs-resources' => ['admin_accounts' => ['url' => '/admin/accounts']]]; - - $this->assertOCSResource($routes, 'admin_accounts', '/apps/app1/admin/accounts', 'AdminAccountsController', 'id'); - } - - public function testOCSResourceWithRoot() { - $routes = ['ocs-resources' => ['admin_accounts' => ['url' => '/admin/accounts', 'root' => '/core/endpoint']]]; - - $this->assertOCSResource($routes, 'admin_accounts', '/core/endpoint/admin/accounts', 'AdminAccountsController', 'id'); - } - - public function testResource() { - $routes = ['resources' => ['account' => ['url' => '/accounts']]]; - - $this->assertResource($routes, 'account', '/apps/app1/accounts', 'AccountController', 'id'); - } - - public function testResourceWithUnderScoreName() { - $routes = ['resources' => ['admin_accounts' => ['url' => '/admin/accounts']]]; - - $this->assertResource($routes, 'admin_accounts', '/apps/app1/admin/accounts', 'AdminAccountsController', 'id'); - } - - private function assertSimpleRoute($routes, $name, $verb, $url, $controllerName, $actionName, array $requirements = [], array $defaults = [], $postfix = '', $allowRootUrl = false): void { - if ($postfix) { - $name .= $postfix; - } - - // route mocks - $container = new DIContainer('app1'); - $route = $this->mockRoute($container, $verb, $controllerName, $actionName, $requirements, $defaults); - - /** @var IRouter|MockObject $router */ - $router = $this->getMockBuilder(Router::class) - ->onlyMethods(['create']) - ->setConstructorArgs([$this->createMock(LoggerInterface::class)]) - ->getMock(); - - // we expect create to be called once: - $router - ->expects($this->once()) - ->method('create') - ->with($this->equalTo('app1.' . $name), $this->equalTo($url)) - ->willReturn($route); - - // load route configuration - $config = new RouteConfig($container, $router, $routes); - if ($allowRootUrl) { - self::invokePrivate($config, 'rootUrlApps', [['app1']]); - } - - $config->register(); - } - - /** - * @param $routes - * @param string $name - * @param string $verb - * @param string $url - * @param string $controllerName - * @param string $actionName - * @param array $requirements - * @param array $defaults - * @param string $postfix - */ - private function assertSimpleOCSRoute($routes, - $name, - $verb, - $url, - $controllerName, - $actionName, - array $requirements = [], - array $defaults = [], - $postfix = '') { - if ($postfix) { - $name .= $postfix; - } - - // route mocks - $container = new DIContainer('app1'); - $route = $this->mockRoute($container, $verb, $controllerName, $actionName, $requirements, $defaults); - - /** @var IRouter|MockObject $router */ - $router = $this->getMockBuilder(Router::class) - ->onlyMethods(['create']) - ->setConstructorArgs([$this->createMock(LoggerInterface::class)]) - ->getMock(); - - // we expect create to be called once: - $router - ->expects($this->once()) - ->method('create') - ->with($this->equalTo('ocs.app1.' . $name), $this->equalTo($url)) - ->willReturn($route); - - // load route configuration - $config = new RouteConfig($container, $router, $routes); - - $config->register(); - } - - /** - * @param array $yaml - * @param string $resourceName - * @param string $url - * @param string $controllerName - * @param string $paramName - */ - private function assertOCSResource($yaml, $resourceName, $url, $controllerName, $paramName): void { - /** @var IRouter|MockObject $router */ - $router = $this->getMockBuilder(Router::class) - ->onlyMethods(['create']) - ->setConstructorArgs([$this->createMock(LoggerInterface::class)]) - ->getMock(); - - // route mocks - $container = new DIContainer('app1'); - $indexRoute = $this->mockRoute($container, 'GET', $controllerName, 'index'); - $showRoute = $this->mockRoute($container, 'GET', $controllerName, 'show'); - $createRoute = $this->mockRoute($container, 'POST', $controllerName, 'create'); - $updateRoute = $this->mockRoute($container, 'PUT', $controllerName, 'update'); - $destroyRoute = $this->mockRoute($container, 'DELETE', $controllerName, 'destroy'); - - $urlWithParam = $url . '/{' . $paramName . '}'; - - // we expect create to be called once: - $router - ->expects($this->at(0)) - ->method('create') - ->with($this->equalTo('ocs.app1.' . $resourceName . '.index'), $this->equalTo($url)) - ->willReturn($indexRoute); - - $router - ->expects($this->at(1)) - ->method('create') - ->with($this->equalTo('ocs.app1.' . $resourceName . '.show'), $this->equalTo($urlWithParam)) - ->willReturn($showRoute); - - $router - ->expects($this->at(2)) - ->method('create') - ->with($this->equalTo('ocs.app1.' . $resourceName . '.create'), $this->equalTo($url)) - ->willReturn($createRoute); - - $router - ->expects($this->at(3)) - ->method('create') - ->with($this->equalTo('ocs.app1.' . $resourceName . '.update'), $this->equalTo($urlWithParam)) - ->willReturn($updateRoute); - - $router - ->expects($this->at(4)) - ->method('create') - ->with($this->equalTo('ocs.app1.' . $resourceName . '.destroy'), $this->equalTo($urlWithParam)) - ->willReturn($destroyRoute); - - // load route configuration - $config = new RouteConfig($container, $router, $yaml); - - $config->register(); - } - - /** - * @param string $resourceName - * @param string $url - * @param string $controllerName - * @param string $paramName - */ - private function assertResource($yaml, $resourceName, $url, $controllerName, $paramName) { - /** @var IRouter|MockObject $router */ - $router = $this->getMockBuilder(Router::class) - ->onlyMethods(['create']) - ->setConstructorArgs([$this->createMock(LoggerInterface::class)]) - ->getMock(); - - // route mocks - $container = new DIContainer('app1'); - $indexRoute = $this->mockRoute($container, 'GET', $controllerName, 'index'); - $showRoute = $this->mockRoute($container, 'GET', $controllerName, 'show'); - $createRoute = $this->mockRoute($container, 'POST', $controllerName, 'create'); - $updateRoute = $this->mockRoute($container, 'PUT', $controllerName, 'update'); - $destroyRoute = $this->mockRoute($container, 'DELETE', $controllerName, 'destroy'); - - $urlWithParam = $url . '/{' . $paramName . '}'; - - // we expect create to be called once: - $router - ->expects($this->at(0)) - ->method('create') - ->with($this->equalTo('app1.' . $resourceName . '.index'), $this->equalTo($url)) - ->willReturn($indexRoute); - - $router - ->expects($this->at(1)) - ->method('create') - ->with($this->equalTo('app1.' . $resourceName . '.show'), $this->equalTo($urlWithParam)) - ->willReturn($showRoute); - - $router - ->expects($this->at(2)) - ->method('create') - ->with($this->equalTo('app1.' . $resourceName . '.create'), $this->equalTo($url)) - ->willReturn($createRoute); - - $router - ->expects($this->at(3)) - ->method('create') - ->with($this->equalTo('app1.' . $resourceName . '.update'), $this->equalTo($urlWithParam)) - ->willReturn($updateRoute); - - $router - ->expects($this->at(4)) - ->method('create') - ->with($this->equalTo('app1.' . $resourceName . '.destroy'), $this->equalTo($urlWithParam)) - ->willReturn($destroyRoute); - - // load route configuration - $config = new RouteConfig($container, $router, $yaml); - - $config->register(); - } - - /** - * @param DIContainer $container - * @param string $verb - * @param string $controllerName - * @param string $actionName - * @param array $requirements - * @param array $defaults - * @return MockObject - */ - private function mockRoute( - DIContainer $container, - $verb, - $controllerName, - $actionName, - array $requirements = [], - array $defaults = [] - ) { - $route = $this->getMockBuilder(Route::class) - ->onlyMethods(['method', 'requirements', 'defaults']) - ->disableOriginalConstructor() - ->getMock(); - $route - ->expects($this->once()) - ->method('method') - ->with($this->equalTo($verb)) - ->willReturn($route); - - if (count($requirements) > 0) { - $route - ->expects($this->once()) - ->method('requirements') - ->with($this->equalTo($requirements)) - ->willReturn($route); - } - - $route->expects($this->once()) - ->method('defaults') - ->with($this->callback(function (array $def) use ($defaults, $controllerName, $actionName) { - $defaults['caller'] = ['app1', $controllerName, $actionName]; - - $this->assertEquals($defaults, $def); - return true; - })) - ->willReturn($route); - - return $route; - } -} diff --git a/tests/lib/AppFramework/Services/AppConfigTest.php b/tests/lib/AppFramework/Services/AppConfigTest.php new file mode 100644 index 00000000000..38fa6bdcac6 --- /dev/null +++ b/tests/lib/AppFramework/Services/AppConfigTest.php @@ -0,0 +1,668 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +namespace Test\AppFramework\Services; + +use OC\AppConfig as AppConfigCore; +use OC\AppFramework\Services\AppConfig; +use OCP\Exceptions\AppConfigTypeConflictException; +use OCP\Exceptions\AppConfigUnknownKeyException; +use OCP\IAppConfig as IAppConfigCore; +use OCP\IConfig; +use PHPUnit\Framework\MockObject\MockObject; +use Test\TestCase; + +class AppConfigTest extends TestCase { + private IConfig|MockObject $config; + private IAppConfigCore|MockObject $appConfigCore; + private AppConfig $appConfig; + + private const TEST_APPID = 'appconfig-test'; + + protected function setUp(): void { + parent::setUp(); + $this->config = $this->createMock(IConfig::class); + $this->appConfigCore = $this->createMock(AppConfigCore::class); + + $this->appConfig = new AppConfig($this->config, $this->appConfigCore, self::TEST_APPID); + } + + public function testGetAppKeys(): void { + $expected = ['key1', 'key2', 'key3', 'key4', 'key5', 'key6', 'key7', 'test8']; + $this->appConfigCore->expects($this->once()) + ->method('getKeys') + ->with(self::TEST_APPID) + ->willReturn($expected); + $this->assertSame($expected, $this->appConfig->getAppKeys()); + } + + + /** + * @return array + * @see testHasAppKey + */ + public static function providerHasAppKey(): array { + return [ + // lazy, expected + [false, true], + [true, true], + [false, false], + [true, false], + ]; + } + + /** + * + * @param bool $lazy + * @param bool $expected + */ + #[\PHPUnit\Framework\Attributes\DataProvider('providerHasAppKey')] + public function testHasAppKey(bool $lazy, bool $expected): void { + $key = 'key'; + $this->appConfigCore->expects($this->once()) + ->method('hasKey') + ->with(self::TEST_APPID, $key, $lazy) + ->willReturn($expected); + $this->assertSame($expected, $this->appConfig->hasAppKey($key, $lazy)); + } + + + /** + * @return array + * @see testIsSensitive + */ + public static function providerIsSensitive(): array { + return [ + // lazy, expected + [false, true], + [true, true], + [false, false], + [true, false], + ]; + } + + /** + * + * @param bool $lazy + * @param bool $expected + */ + #[\PHPUnit\Framework\Attributes\DataProvider('providerIsSensitive')] + public function testIsSensitive(bool $lazy, bool $expected): void { + $key = 'key'; + $this->appConfigCore->expects($this->once()) + ->method('isSensitive') + ->with(self::TEST_APPID, $key, $lazy) + ->willReturn($expected); + + $this->assertSame($expected, $this->appConfig->isSensitive($key, $lazy)); + } + + /** + * + * @param bool $lazy + * @param bool $expected + */ + #[\PHPUnit\Framework\Attributes\DataProvider('providerIsSensitive')] + public function testIsSensitiveException(bool $lazy, bool $expected): void { + $key = 'unknown-key'; + $this->appConfigCore->expects($this->once()) + ->method('isSensitive') + ->with(self::TEST_APPID, $key, $lazy) + ->willThrowException(new AppConfigUnknownKeyException()); + + $this->expectException(AppConfigUnknownKeyException::class); + $this->appConfig->isSensitive($key, $lazy); + } + + /** + * @return array + * @see testIsLazy + */ + public static function providerIsLazy(): array { + return [ + // expected + [true], + [false], + ]; + } + + /** + * @param bool $expected + */ + #[\PHPUnit\Framework\Attributes\DataProvider('providerIsLazy')] + public function testIsLazy(bool $expected): void { + $key = 'key'; + $this->appConfigCore->expects($this->once()) + ->method('isLazy') + ->with(self::TEST_APPID, $key) + ->willReturn($expected); + + $this->assertSame($expected, $this->appConfig->isLazy($key)); + } + + public function testIsLazyException(): void { + $key = 'unknown-key'; + $this->appConfigCore->expects($this->once()) + ->method('isLazy') + ->with(self::TEST_APPID, $key) + ->willThrowException(new AppConfigUnknownKeyException()); + + $this->expectException(AppConfigUnknownKeyException::class); + $this->appConfig->isLazy($key); + } + + /** + * @return array + * @see testGetAllAppValues + */ + public static function providerGetAllAppValues(): array { + return [ + // key, filtered + ['', false], + ['', true], + ['key', false], + ['key', true], + ]; + } + + /** + * + * @param string $key + * @param bool $filtered + */ + #[\PHPUnit\Framework\Attributes\DataProvider('providerGetAllAppValues')] + public function testGetAllAppValues(string $key, bool $filtered): void { + $expected = [ + 'key1' => 'value1', + 'key2' => 3, + 'key3' => 3.14, + 'key4' => true + ]; + + $this->appConfigCore->expects($this->once()) + ->method('getAllValues') + ->with(self::TEST_APPID, $key, $filtered) + ->willReturn($expected); + + $this->assertSame($expected, $this->appConfig->getAllAppValues($key, $filtered)); + } + + public function testSetAppValue(): void { + $key = 'key'; + $value = 'value'; + $this->appConfigCore->expects($this->once()) + ->method('setValueMixed') + ->with(self::TEST_APPID, $key, $value); + + $this->appConfig->setAppValue($key, $value); + } + + /** + * @return array + * @see testSetAppValueString + * @see testSetAppValueStringException + * @see testSetAppValueInt + * @see testSetAppValueIntException + * @see testSetAppValueFloat + * @see testSetAppValueFloatException + * @see testSetAppValueArray + * @see testSetAppValueArrayException + */ + public static function providerSetAppValue(): array { + return [ + // lazy, sensitive, expected + [false, false, true], + [false, true, true], + [true, true, true], + [true, false, true], + [false, false, false], + [false, true, false], + [true, true, false], + [true, false, false], + ]; + } + + /** + * + * @param bool $lazy + * @param bool $sensitive + * @param bool $expected + */ + #[\PHPUnit\Framework\Attributes\DataProvider('providerSetAppValue')] + public function testSetAppValueString(bool $lazy, bool $sensitive, bool $expected): void { + $key = 'key'; + $value = 'valueString'; + $this->appConfigCore->expects($this->once()) + ->method('setValueString') + ->with(self::TEST_APPID, $key, $value, $lazy, $sensitive) + ->willReturn($expected); + + $this->assertSame($expected, $this->appConfig->setAppValueString($key, $value, $lazy, $sensitive)); + } + + /** + * + * @param bool $lazy + * @param bool $sensitive + */ + #[\PHPUnit\Framework\Attributes\DataProvider('providerSetAppValue')] + public function testSetAppValueStringException(bool $lazy, bool $sensitive): void { + $key = 'key'; + $value = 'valueString'; + $this->appConfigCore->expects($this->once()) + ->method('setValueString') + ->with(self::TEST_APPID, $key, $value, $lazy, $sensitive) + ->willThrowException(new AppConfigTypeConflictException()); + + $this->expectException(AppConfigTypeConflictException::class); + $this->appConfig->setAppValueString($key, $value, $lazy, $sensitive); + } + + /** + * + * @param bool $lazy + * @param bool $sensitive + * @param bool $expected + */ + #[\PHPUnit\Framework\Attributes\DataProvider('providerSetAppValue')] + public function testSetAppValueInt(bool $lazy, bool $sensitive, bool $expected): void { + $key = 'key'; + $value = 42; + $this->appConfigCore->expects($this->once()) + ->method('setValueInt') + ->with(self::TEST_APPID, $key, $value, $lazy, $sensitive) + ->willReturn($expected); + + $this->assertSame($expected, $this->appConfig->setAppValueInt($key, $value, $lazy, $sensitive)); + } + + /** + * + * @param bool $lazy + * @param bool $sensitive + */ + #[\PHPUnit\Framework\Attributes\DataProvider('providerSetAppValue')] + public function testSetAppValueIntException(bool $lazy, bool $sensitive): void { + $key = 'key'; + $value = 42; + $this->appConfigCore->expects($this->once()) + ->method('setValueInt') + ->with(self::TEST_APPID, $key, $value, $lazy, $sensitive) + ->willThrowException(new AppConfigTypeConflictException()); + + $this->expectException(AppConfigTypeConflictException::class); + $this->appConfig->setAppValueInt($key, $value, $lazy, $sensitive); + } + + /** + * + * @param bool $lazy + * @param bool $sensitive + * @param bool $expected + */ + #[\PHPUnit\Framework\Attributes\DataProvider('providerSetAppValue')] + public function testSetAppValueFloat(bool $lazy, bool $sensitive, bool $expected): void { + $key = 'key'; + $value = 3.14; + $this->appConfigCore->expects($this->once()) + ->method('setValueFloat') + ->with(self::TEST_APPID, $key, $value, $lazy, $sensitive) + ->willReturn($expected); + + $this->assertSame($expected, $this->appConfig->setAppValueFloat($key, $value, $lazy, $sensitive)); + } + + /** + * + * @param bool $lazy + * @param bool $sensitive + */ + #[\PHPUnit\Framework\Attributes\DataProvider('providerSetAppValue')] + public function testSetAppValueFloatException(bool $lazy, bool $sensitive): void { + $key = 'key'; + $value = 3.14; + $this->appConfigCore->expects($this->once()) + ->method('setValueFloat') + ->with(self::TEST_APPID, $key, $value, $lazy, $sensitive) + ->willThrowException(new AppConfigTypeConflictException()); + + $this->expectException(AppConfigTypeConflictException::class); + $this->appConfig->setAppValueFloat($key, $value, $lazy, $sensitive); + } + + /** + * @return array + * @see testSetAppValueBool + */ + public static function providerSetAppValueBool(): array { + return [ + // lazy, expected + [false, true], + [false, false], + [true, true], + [true, false], + ]; + } + + /** + * + * @param bool $lazy + * @param bool $expected + */ + #[\PHPUnit\Framework\Attributes\DataProvider('providerSetAppValueBool')] + public function testSetAppValueBool(bool $lazy, bool $expected): void { + $key = 'key'; + $value = true; + $this->appConfigCore->expects($this->once()) + ->method('setValueBool') + ->with(self::TEST_APPID, $key, $value, $lazy) + ->willReturn($expected); + + $this->assertSame($expected, $this->appConfig->setAppValueBool($key, $value, $lazy)); + } + + /** + * @param bool $lazy + */ + #[\PHPUnit\Framework\Attributes\DataProvider('providerSetAppValueBool')] + public function testSetAppValueBoolException(bool $lazy): void { + $key = 'key'; + $value = true; + $this->appConfigCore->expects($this->once()) + ->method('setValueBool') + ->with(self::TEST_APPID, $key, $value, $lazy) + ->willThrowException(new AppConfigTypeConflictException()); + + $this->expectException(AppConfigTypeConflictException::class); + $this->appConfig->setAppValueBool($key, $value, $lazy); + } + + /** + * + * @param bool $lazy + * @param bool $sensitive + * @param bool $expected + */ + #[\PHPUnit\Framework\Attributes\DataProvider('providerSetAppValue')] + public function testSetAppValueArray(bool $lazy, bool $sensitive, bool $expected): void { + $key = 'key'; + $value = ['item' => true]; + $this->appConfigCore->expects($this->once()) + ->method('setValueArray') + ->with(self::TEST_APPID, $key, $value, $lazy, $sensitive) + ->willReturn($expected); + + $this->assertSame($expected, $this->appConfig->setAppValueArray($key, $value, $lazy, $sensitive)); + } + + /** + * + * @param bool $lazy + * @param bool $sensitive + */ + #[\PHPUnit\Framework\Attributes\DataProvider('providerSetAppValue')] + public function testSetAppValueArrayException(bool $lazy, bool $sensitive): void { + $key = 'key'; + $value = ['item' => true]; + $this->appConfigCore->expects($this->once()) + ->method('setValueArray') + ->with(self::TEST_APPID, $key, $value, $lazy, $sensitive) + ->willThrowException(new AppConfigTypeConflictException()); + + $this->expectException(AppConfigTypeConflictException::class); + $this->appConfig->setAppValueArray($key, $value, $lazy, $sensitive); + } + + public function testGetAppValue(): void { + $key = 'key'; + $value = 'value'; + $default = 'default'; + $this->appConfigCore->expects($this->once()) + ->method('getValueMixed') + ->with(self::TEST_APPID, $key, $default) + ->willReturn($value); + + $this->assertSame($value, $this->appConfig->getAppValue($key, $default)); + } + + public function testGetAppValueDefault(): void { + $key = 'key'; + $default = 'default'; + $this->appConfigCore->expects($this->once()) + ->method('getValueMixed') + ->with(self::TEST_APPID, $key, $default) + ->willReturn($default); + + $this->assertSame($default, $this->appConfig->getAppValue($key, $default)); + } + + /** + * @return array + * @see testGetAppValueString + * @see testGetAppValueStringException + * @see testGetAppValueInt + * @see testGetAppValueIntException + * @see testGetAppValueFloat + * @see testGetAppValueFloatException + * @see testGetAppValueBool + * @see testGetAppValueBoolException + * @see testGetAppValueArray + * @see testGetAppValueArrayException + */ + public static function providerGetAppValue(): array { + return [ + // lazy, exist + [false, false], + [false, true], + [true, true], + [true, false] + ]; + } + + /** + * + * @param bool $lazy + * @param bool $exist + */ + #[\PHPUnit\Framework\Attributes\DataProvider('providerGetAppValue')] + public function testGetAppValueString(bool $lazy, bool $exist): void { + $key = 'key'; + $value = 'valueString'; + $default = 'default'; + + $expected = ($exist) ? $value : $default; + $this->appConfigCore->expects($this->once()) + ->method('getValueString') + ->with(self::TEST_APPID, $key, $default, $lazy) + ->willReturn($expected); + + $this->assertSame($expected, $this->appConfig->getAppValueString($key, $default, $lazy)); + } + + /** + * @param bool $lazy + */ + #[\PHPUnit\Framework\Attributes\DataProvider('providerGetAppValue')] + public function testGetAppValueStringException(bool $lazy): void { + $key = 'key'; + $default = 'default'; + + $this->appConfigCore->expects($this->once()) + ->method('getValueString') + ->with(self::TEST_APPID, $key, $default, $lazy) + ->willThrowException(new AppConfigTypeConflictException()); + + $this->expectException(AppConfigTypeConflictException::class); + $this->appConfig->getAppValueString($key, $default, $lazy); + } + + /** + * + * @param bool $lazy + * @param bool $exist + */ + #[\PHPUnit\Framework\Attributes\DataProvider('providerGetAppValue')] + public function testGetAppValueInt(bool $lazy, bool $exist): void { + $key = 'key'; + $value = 42; + $default = 17; + + $expected = ($exist) ? $value : $default; + $this->appConfigCore->expects($this->once()) + ->method('getValueInt') + ->with(self::TEST_APPID, $key, $default, $lazy) + ->willReturn($expected); + + $this->assertSame($expected, $this->appConfig->getAppValueInt($key, $default, $lazy)); + } + + /** + * @param bool $lazy + */ + #[\PHPUnit\Framework\Attributes\DataProvider('providerGetAppValue')] + public function testGetAppValueIntException(bool $lazy): void { + $key = 'key'; + $default = 17; + + $this->appConfigCore->expects($this->once()) + ->method('getValueInt') + ->with(self::TEST_APPID, $key, $default, $lazy) + ->willThrowException(new AppConfigTypeConflictException()); + + $this->expectException(AppConfigTypeConflictException::class); + $this->appConfig->getAppValueInt($key, $default, $lazy); + } + + /** + * + * @param bool $lazy + * @param bool $exist + */ + #[\PHPUnit\Framework\Attributes\DataProvider('providerGetAppValue')] + public function testGetAppValueFloat(bool $lazy, bool $exist): void { + $key = 'key'; + $value = 3.14; + $default = 17.04; + + $expected = ($exist) ? $value : $default; + $this->appConfigCore->expects($this->once()) + ->method('getValueFloat') + ->with(self::TEST_APPID, $key, $default, $lazy) + ->willReturn($expected); + + $this->assertSame($expected, $this->appConfig->getAppValueFloat($key, $default, $lazy)); + } + + /** + * @param bool $lazy + */ + #[\PHPUnit\Framework\Attributes\DataProvider('providerGetAppValue')] + public function testGetAppValueFloatException(bool $lazy): void { + $key = 'key'; + $default = 17.04; + + $this->appConfigCore->expects($this->once()) + ->method('getValueFloat') + ->with(self::TEST_APPID, $key, $default, $lazy) + ->willThrowException(new AppConfigTypeConflictException()); + + $this->expectException(AppConfigTypeConflictException::class); + $this->appConfig->getAppValueFloat($key, $default, $lazy); + } + + /** + * + * @param bool $lazy + * @param bool $exist + */ + #[\PHPUnit\Framework\Attributes\DataProvider('providerGetAppValue')] + public function testGetAppValueBool(bool $lazy, bool $exist): void { + $key = 'key'; + $value = true; + $default = false; + + $expected = ($exist) ? $value : $default; // yes, it can be simplified + $this->appConfigCore->expects($this->once()) + ->method('getValueBool') + ->with(self::TEST_APPID, $key, $default, $lazy) + ->willReturn($expected); + + $this->assertSame($expected, $this->appConfig->getAppValueBool($key, $default, $lazy)); + } + + /** + * @param bool $lazy + */ + #[\PHPUnit\Framework\Attributes\DataProvider('providerGetAppValue')] + public function testGetAppValueBoolException(bool $lazy): void { + $key = 'key'; + $default = false; + + $this->appConfigCore->expects($this->once()) + ->method('getValueBool') + ->with(self::TEST_APPID, $key, $default, $lazy) + ->willThrowException(new AppConfigTypeConflictException()); + + $this->expectException(AppConfigTypeConflictException::class); + $this->appConfig->getAppValueBool($key, $default, $lazy); + } + + /** + * + * @param bool $lazy + * @param bool $exist + */ + #[\PHPUnit\Framework\Attributes\DataProvider('providerGetAppValue')] + public function testGetAppValueArray(bool $lazy, bool $exist): void { + $key = 'key'; + $value = ['item' => true]; + $default = []; + + $expected = ($exist) ? $value : $default; + $this->appConfigCore->expects($this->once()) + ->method('getValueArray') + ->with(self::TEST_APPID, $key, $default, $lazy) + ->willReturn($expected); + + $this->assertSame($expected, $this->appConfig->getAppValueArray($key, $default, $lazy)); + } + + /** + * @param bool $lazy + */ + #[\PHPUnit\Framework\Attributes\DataProvider('providerGetAppValue')] + public function testGetAppValueArrayException(bool $lazy): void { + $key = 'key'; + $default = []; + + $this->appConfigCore->expects($this->once()) + ->method('getValueArray') + ->with(self::TEST_APPID, $key, $default, $lazy) + ->willThrowException(new AppConfigTypeConflictException()); + + $this->expectException(AppConfigTypeConflictException::class); + $this->appConfig->getAppValueArray($key, $default, $lazy); + } + + public function testDeleteAppValue(): void { + $key = 'key'; + $this->appConfigCore->expects($this->once()) + ->method('deleteKey') + ->with(self::TEST_APPID, $key); + + $this->appConfig->deleteAppValue($key); + } + + public function testDeleteAppValues(): void { + $this->appConfigCore->expects($this->once()) + ->method('deleteApp') + ->with(self::TEST_APPID); + + $this->appConfig->deleteAppValues(); + } +} diff --git a/tests/lib/AppFramework/Utility/ControllerMethodReflectorTest.php b/tests/lib/AppFramework/Utility/ControllerMethodReflectorTest.php index 6bf683ef2ed..00ae4792824 100644 --- a/tests/lib/AppFramework/Utility/ControllerMethodReflectorTest.php +++ b/tests/lib/AppFramework/Utility/ControllerMethodReflectorTest.php @@ -1,24 +1,9 @@ <?php /** - * ownCloud - App Framework - * - * @author Bernhard Posselt - * @copyright 2012 Bernhard Posselt <dev@bernhard-posselt.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\AppFramework\Utility; @@ -26,7 +11,6 @@ namespace Test\AppFramework\Utility; use OC\AppFramework\Utility\ControllerMethodReflector; class BaseController { - /** * @Annotation */ @@ -47,7 +31,6 @@ class BaseController { } class MiddleController extends BaseController { - /** * @NoAnnotation */ @@ -56,18 +39,26 @@ class MiddleController extends BaseController { public function test3() { } + + /** + * @psalm-param int<-4, 42> $rangedOne + * @psalm-param int<min, max> $rangedTwo + * @psalm-param int<1, 6>|null $rangedThree + * @psalm-param ?int<-70, -30> $rangedFour + * @return void + */ + public function test4(int $rangedOne, int $rangedTwo, ?int $rangedThree, ?int $rangedFour) { + } } class EndController extends MiddleController { } class ControllerMethodReflectorTest extends \Test\TestCase { - - /** * @Annotation */ - public function testReadAnnotation() { + public function testReadAnnotation(): void { $reader = new ControllerMethodReflector(); $reader->reflect( '\Test\AppFramework\Utility\ControllerMethodReflectorTest', @@ -80,10 +71,10 @@ class ControllerMethodReflectorTest extends \Test\TestCase { /** * @Annotation(parameter=value) */ - public function testGetAnnotationParameterSingle() { + public function testGetAnnotationParameterSingle(): void { $reader = new ControllerMethodReflector(); $reader->reflect( - __CLASS__, + self::class, __FUNCTION__ ); @@ -93,10 +84,10 @@ class ControllerMethodReflectorTest extends \Test\TestCase { /** * @Annotation(parameter1=value1, parameter2=value2,parameter3=value3) */ - public function testGetAnnotationParameterMultiple() { + public function testGetAnnotationParameterMultiple(): void { $reader = new ControllerMethodReflector(); $reader->reflect( - __CLASS__, + self::class, __FUNCTION__ ); @@ -109,7 +100,7 @@ class ControllerMethodReflectorTest extends \Test\TestCase { * @Annotation * @param test */ - public function testReadAnnotationNoLowercase() { + public function testReadAnnotationNoLowercase(): void { $reader = new ControllerMethodReflector(); $reader->reflect( '\Test\AppFramework\Utility\ControllerMethodReflectorTest', @@ -125,7 +116,7 @@ class ControllerMethodReflectorTest extends \Test\TestCase { * @Annotation * @param int $test */ - public function testReadTypeIntAnnotations() { + public function testReadTypeIntAnnotations(): void { $reader = new ControllerMethodReflector(); $reader->reflect( '\Test\AppFramework\Utility\ControllerMethodReflectorTest', @@ -146,7 +137,7 @@ class ControllerMethodReflectorTest extends \Test\TestCase { /** * @requires PHP 7 */ - public function testReadTypeIntAnnotationsScalarTypes() { + public function testReadTypeIntAnnotationsScalarTypes(): void { $reader = new ControllerMethodReflector(); $reader->reflect( '\Test\AppFramework\Utility\ControllerMethodReflectorTest', @@ -164,7 +155,7 @@ class ControllerMethodReflectorTest extends \Test\TestCase { * @Annotation * @param double $test something special */ - public function testReadTypeDoubleAnnotations() { + public function testReadTypeDoubleAnnotations(): void { $reader = new ControllerMethodReflector(); $reader->reflect( '\Test\AppFramework\Utility\ControllerMethodReflectorTest', @@ -176,9 +167,9 @@ class ControllerMethodReflectorTest extends \Test\TestCase { /** * @Annotation - * @param string $foo + * @param string $foo */ - public function testReadTypeWhitespaceAnnotations() { + public function testReadTypeWhitespaceAnnotations(): void { $reader = new ControllerMethodReflector(); $reader->reflect( '\Test\AppFramework\Utility\ControllerMethodReflectorTest', @@ -191,7 +182,7 @@ class ControllerMethodReflectorTest extends \Test\TestCase { public function arguments($arg, $arg2 = 'hi') { } - public function testReflectParameters() { + public function testReflectParameters(): void { $reader = new ControllerMethodReflector(); $reader->reflect( '\Test\AppFramework\Utility\ControllerMethodReflectorTest', @@ -204,7 +195,7 @@ class ControllerMethodReflectorTest extends \Test\TestCase { public function arguments2($arg) { } - public function testReflectParameters2() { + public function testReflectParameters2(): void { $reader = new ControllerMethodReflector(); $reader->reflect( '\Test\AppFramework\Utility\ControllerMethodReflectorTest', @@ -215,7 +206,7 @@ class ControllerMethodReflectorTest extends \Test\TestCase { } - public function testInheritance() { + public function testInheritance(): void { $reader = new ControllerMethodReflector(); $reader->reflect('Test\AppFramework\Utility\EndController', 'test'); @@ -223,7 +214,7 @@ class ControllerMethodReflectorTest extends \Test\TestCase { } - public function testInheritanceOverride() { + public function testInheritanceOverride(): void { $reader = new ControllerMethodReflector(); $reader->reflect('Test\AppFramework\Utility\EndController', 'test2'); @@ -232,10 +223,31 @@ class ControllerMethodReflectorTest extends \Test\TestCase { } - public function testInheritanceOverrideNoDocblock() { + public function testInheritanceOverrideNoDocblock(): void { $reader = new ControllerMethodReflector(); $reader->reflect('Test\AppFramework\Utility\EndController', 'test3'); $this->assertFalse($reader->hasAnnotation('Annotation')); } + + public function testRangeDetection(): void { + $reader = new ControllerMethodReflector(); + $reader->reflect('Test\AppFramework\Utility\EndController', 'test4'); + + $rangeInfo1 = $reader->getRange('rangedOne'); + $this->assertSame(-4, $rangeInfo1['min']); + $this->assertSame(42, $rangeInfo1['max']); + + $rangeInfo2 = $reader->getRange('rangedTwo'); + $this->assertSame(PHP_INT_MIN, $rangeInfo2['min']); + $this->assertSame(PHP_INT_MAX, $rangeInfo2['max']); + + $rangeInfo3 = $reader->getRange('rangedThree'); + $this->assertSame(1, $rangeInfo3['min']); + $this->assertSame(6, $rangeInfo3['max']); + + $rangeInfo3 = $reader->getRange('rangedFour'); + $this->assertSame(-70, $rangeInfo3['min']); + $this->assertSame(-30, $rangeInfo3['max']); + } } diff --git a/tests/lib/AppFramework/Utility/SimpleContainerTest.php b/tests/lib/AppFramework/Utility/SimpleContainerTest.php index 36fa1febfcf..33800c7376f 100644 --- a/tests/lib/AppFramework/Utility/SimpleContainerTest.php +++ b/tests/lib/AppFramework/Utility/SimpleContainerTest.php @@ -3,29 +3,16 @@ declare(strict_types=1); /** - * ownCloud - App Framework - * - * @author Bernhard Posselt - * @copyright 2014 Bernhard Posselt <dev@bernhard-posselt.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace Test\AppFramework\Utility; use OC\AppFramework\Utility\SimpleContainer; +use OCP\AppFramework\QueryException; +use Psr\Container\NotFoundExceptionInterface; interface TestInterface { } @@ -34,29 +21,40 @@ class ClassEmptyConstructor implements IInterfaceConstructor { } class ClassSimpleConstructor implements IInterfaceConstructor { - public $test; - public function __construct($test) { - $this->test = $test; + public function __construct( + public $test, + ) { } } class ClassComplexConstructor { - public $class; - public $test; - public function __construct(ClassSimpleConstructor $class, $test) { - $this->class = $class; - $this->test = $test; + public function __construct( + public ClassSimpleConstructor $class, + public $test, + ) { + } +} + +class ClassNullableUntypedConstructorArg { + public function __construct( + public $class, + ) { + } +} +class ClassNullableTypedConstructorArg { + public function __construct( + public ?\Some\Class $class, + ) { } } interface IInterfaceConstructor { } class ClassInterfaceConstructor { - public $class; - public $test; - public function __construct(IInterfaceConstructor $class, $test) { - $this->class = $class; - $this->test = $test; + public function __construct( + public IInterfaceConstructor $class, + public $test, + ) { } } @@ -70,41 +68,61 @@ class SimpleContainerTest extends \Test\TestCase { - public function testRegister() { + public function testRegister(): void { $this->container->registerParameter('test', 'abc'); $this->assertEquals('abc', $this->container->query('test')); } + /** + * Test querying a class that is not registered without autoload enabled + */ + public function testNothingRegistered(): void { + try { + $this->container->query('something really hard', false); + $this->fail('Expected `QueryException` exception was not thrown'); + } catch (\Throwable $exception) { + $this->assertInstanceOf(QueryException::class, $exception); + $this->assertInstanceOf(NotFoundExceptionInterface::class, $exception); + } + } - public function testNothingRegistered() { - $this->expectException(\OCP\AppFramework\QueryException::class); - $this->container->query('something really hard'); + /** + * Test querying a class that is not registered with autoload enabled + */ + public function testNothingRegistered_autoload(): void { + try { + $this->container->query('something really hard'); + $this->fail('Expected `QueryException` exception was not thrown'); + } catch (\Throwable $exception) { + $this->assertInstanceOf(QueryException::class, $exception); + $this->assertInstanceOf(NotFoundExceptionInterface::class, $exception); + } } - public function testNotAClass() { - $this->expectException(\OCP\AppFramework\QueryException::class); + public function testNotAClass(): void { + $this->expectException(QueryException::class); $this->container->query('Test\AppFramework\Utility\TestInterface'); } - public function testNoConstructorClass() { + public function testNoConstructorClass(): void { $object = $this->container->query('Test\AppFramework\Utility\ClassEmptyConstructor'); $this->assertTrue($object instanceof ClassEmptyConstructor); } - public function testInstancesOnlyOnce() { + public function testInstancesOnlyOnce(): void { $object = $this->container->query('Test\AppFramework\Utility\ClassEmptyConstructor'); $object2 = $this->container->query('Test\AppFramework\Utility\ClassEmptyConstructor'); $this->assertSame($object, $object2); } - public function testConstructorSimple() { + public function testConstructorSimple(): void { $this->container->registerParameter('test', 'abc'); $object = $this->container->query( 'Test\AppFramework\Utility\ClassSimpleConstructor' @@ -114,7 +132,7 @@ class SimpleContainerTest extends \Test\TestCase { } - public function testConstructorComplex() { + public function testConstructorComplex(): void { $this->container->registerParameter('test', 'abc'); $object = $this->container->query( 'Test\AppFramework\Utility\ClassComplexConstructor' @@ -125,12 +143,12 @@ class SimpleContainerTest extends \Test\TestCase { } - public function testConstructorComplexInterface() { + public function testConstructorComplexInterface(): void { $this->container->registerParameter('test', 'abc'); $this->container->registerService( - 'Test\AppFramework\Utility\IInterfaceConstructor', function ($c) { - return $c->query('Test\AppFramework\Utility\ClassSimpleConstructor'); - }); + 'Test\AppFramework\Utility\IInterfaceConstructor', function ($c) { + return $c->query('Test\AppFramework\Utility\ClassSimpleConstructor'); + }); $object = $this->container->query( 'Test\AppFramework\Utility\ClassInterfaceConstructor' ); @@ -140,28 +158,28 @@ class SimpleContainerTest extends \Test\TestCase { } - public function testOverrideService() { + public function testOverrideService(): void { $this->container->registerService( - 'Test\AppFramework\Utility\IInterfaceConstructor', function ($c) { - return $c->query('Test\AppFramework\Utility\ClassSimpleConstructor'); - }); + 'Test\AppFramework\Utility\IInterfaceConstructor', function ($c) { + return $c->query('Test\AppFramework\Utility\ClassSimpleConstructor'); + }); $this->container->registerService( - 'Test\AppFramework\Utility\IInterfaceConstructor', function ($c) { - return $c->query('Test\AppFramework\Utility\ClassEmptyConstructor'); - }); + 'Test\AppFramework\Utility\IInterfaceConstructor', function ($c) { + return $c->query('Test\AppFramework\Utility\ClassEmptyConstructor'); + }); $object = $this->container->query( 'Test\AppFramework\Utility\IInterfaceConstructor' ); $this->assertTrue($object instanceof ClassEmptyConstructor); } - public function testRegisterAliasParamter() { + public function testRegisterAliasParamter(): void { $this->container->registerParameter('test', 'abc'); $this->container->registerAlias('test1', 'test'); $this->assertEquals('abc', $this->container->query('test1')); } - public function testRegisterAliasService() { + public function testRegisterAliasService(): void { $this->container->registerService('test', function () { return new \StdClass; }, true); @@ -174,7 +192,7 @@ class SimpleContainerTest extends \Test\TestCase { $this->container->query('test'), $this->container->query('test1')); } - public function sanitizeNameProvider() { + public static function sanitizeNameProvider(): array { return [ ['ABC\\Foo', 'ABC\\Foo'], ['\\ABC\\Foo', '\\ABC\\Foo'], @@ -183,10 +201,8 @@ class SimpleContainerTest extends \Test\TestCase { ]; } - /** - * @dataProvider sanitizeNameProvider - */ - public function testSanitizeName($register, $query) { + #[\PHPUnit\Framework\Attributes\DataProvider('sanitizeNameProvider')] + public function testSanitizeName($register, $query): void { $this->container->registerService($register, function () { return 'abc'; }); @@ -194,15 +210,17 @@ class SimpleContainerTest extends \Test\TestCase { } - public function testConstructorComplexNoTestParameterFound() { - $this->expectException(\OCP\AppFramework\QueryException::class); + public function testConstructorComplexNoTestParameterFound(): void { + $this->expectException(QueryException::class); $object = $this->container->query( 'Test\AppFramework\Utility\ClassComplexConstructor' ); + /* Use the object to trigger DI on PHP >= 8.4 */ + get_object_vars($object); } - public function testRegisterFactory() { + public function testRegisterFactory(): void { $this->container->registerService('test', function () { return new \StdClass(); }, false); @@ -210,7 +228,7 @@ class SimpleContainerTest extends \Test\TestCase { $this->container->query('test'), $this->container->query('test')); } - public function testRegisterAliasFactory() { + public function testRegisterAliasFactory(): void { $this->container->registerService('test', function () { return new \StdClass(); }, false); @@ -222,4 +240,21 @@ class SimpleContainerTest extends \Test\TestCase { $this->assertNotSame( $this->container->query('test'), $this->container->query('test1')); } + + public function testQueryUntypedNullable(): void { + $this->expectException(QueryException::class); + + $object = $this->container->query( + ClassNullableUntypedConstructorArg::class + ); + /* Use the object to trigger DI on PHP >= 8.4 */ + get_object_vars($object); + } + + public function testQueryTypedNullable(): void { + /** @var ClassNullableTypedConstructorArg $service */ + $service = $this->container->query(ClassNullableTypedConstructorArg::class); + + self::assertNull($service->class); + } } diff --git a/tests/lib/AppFramework/Utility/TimeFactoryTest.php b/tests/lib/AppFramework/Utility/TimeFactoryTest.php new file mode 100644 index 00000000000..276b2d6da4f --- /dev/null +++ b/tests/lib/AppFramework/Utility/TimeFactoryTest.php @@ -0,0 +1,50 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +namespace Test\AppFramework\Utility; + +use OC\AppFramework\Utility\TimeFactory; + +class TimeFactoryTest extends \Test\TestCase { + protected TimeFactory $timeFactory; + + protected function setUp(): void { + $this->timeFactory = new TimeFactory(); + } + + public function testNow(): void { + $now = $this->timeFactory->now(); + self::assertSame('UTC', $now->getTimezone()->getName()); + } + + public function testNowWithTimeZone(): void { + $timezone = new \DateTimeZone('Europe/Berlin'); + $withTimeZone = $this->timeFactory->withTimeZone($timezone); + + $now = $withTimeZone->now(); + self::assertSame('Europe/Berlin', $now->getTimezone()->getName()); + } + + public function testGetTimeZone(): void { + $expected = new \DateTimeZone('Europe/Berlin'); + $actual = $this->timeFactory->getTimeZone('Europe/Berlin'); + self::assertEquals($expected, $actual); + } + + public function testGetTimeZoneUTC(): void { + $expected = new \DateTimeZone('UTC'); + $actual = $this->timeFactory->getTimeZone(); + self::assertEquals($expected, $actual); + } + + public function testGetTimeZoneInvalid(): void { + $this->expectException(\Exception::class); + $this->timeFactory->getTimeZone('blubblub'); + } +} |