diff options
Diffstat (limited to 'tests/lib/AppFramework/Middleware')
5 files changed, 1175 insertions, 0 deletions
diff --git a/tests/lib/AppFramework/Middleware/MiddlewareDispatcherTest.php b/tests/lib/AppFramework/Middleware/MiddlewareDispatcherTest.php new file mode 100644 index 00000000000..f81aca106d6 --- /dev/null +++ b/tests/lib/AppFramework/Middleware/MiddlewareDispatcherTest.php @@ -0,0 +1,292 @@ +<?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\Middleware; + +use OC\AppFramework\Http\Request; +use OC\AppFramework\Middleware\MiddlewareDispatcher; +use OCP\AppFramework\Middleware; +use OCP\AppFramework\Http\Response; + + +// needed to test ordering +class TestMiddleware extends Middleware { + public static $beforeControllerCalled = 0; + public static $afterControllerCalled = 0; + public static $afterExceptionCalled = 0; + public static $beforeOutputCalled = 0; + + public $beforeControllerOrder = 0; + public $afterControllerOrder = 0; + public $afterExceptionOrder = 0; + public $beforeOutputOrder = 0; + + public $controller; + public $methodName; + public $exception; + public $response; + public $output; + + private $beforeControllerThrowsEx; + + /** + * @param boolean $beforeControllerThrowsEx + */ + public function __construct($beforeControllerThrowsEx) { + self::$beforeControllerCalled = 0; + self::$afterControllerCalled = 0; + self::$afterExceptionCalled = 0; + self::$beforeOutputCalled = 0; + $this->beforeControllerThrowsEx = $beforeControllerThrowsEx; + } + + public function beforeController($controller, $methodName){ + self::$beforeControllerCalled++; + $this->beforeControllerOrder = self::$beforeControllerCalled; + $this->controller = $controller; + $this->methodName = $methodName; + if($this->beforeControllerThrowsEx){ + throw new \Exception(); + } + } + + public function afterException($controller, $methodName, \Exception $exception){ + self::$afterExceptionCalled++; + $this->afterExceptionOrder = self::$afterExceptionCalled; + $this->controller = $controller; + $this->methodName = $methodName; + $this->exception = $exception; + parent::afterException($controller, $methodName, $exception); + } + + public function afterController($controller, $methodName, Response $response){ + self::$afterControllerCalled++; + $this->afterControllerOrder = self::$afterControllerCalled; + $this->controller = $controller; + $this->methodName = $methodName; + $this->response = $response; + return parent::afterController($controller, $methodName, $response); + } + + public function beforeOutput($controller, $methodName, $output){ + self::$beforeOutputCalled++; + $this->beforeOutputOrder = self::$beforeOutputCalled; + $this->controller = $controller; + $this->methodName = $methodName; + $this->output = $output; + return parent::beforeOutput($controller, $methodName, $output); + } +} + + +class MiddlewareDispatcherTest extends \Test\TestCase { + + public $exception; + public $response; + private $out; + private $method; + private $controller; + + /** + * @var MiddlewareDispatcher + */ + private $dispatcher; + + protected function setUp() { + parent::setUp(); + + $this->dispatcher = new MiddlewareDispatcher(); + $this->controller = $this->getControllerMock(); + $this->method = 'method'; + $this->response = new Response(); + $this->out = 'hi'; + $this->exception = new \Exception(); + } + + + private function getControllerMock(){ + return $this->getMock( + 'OCP\AppFramework\Controller', + ['method'], + ['app', + new Request( + ['method' => 'GET'], + $this->getMock('\OCP\Security\ISecureRandom'), + $this->getMock('\OCP\IConfig') + ) + ] + ); + } + + + private function getMiddleware($beforeControllerThrowsEx=false){ + $m1 = new TestMiddleware($beforeControllerThrowsEx); + $this->dispatcher->registerMiddleware($m1); + return $m1; + } + + + public function testAfterExceptionShouldReturnResponseOfMiddleware(){ + $response = new Response(); + $m1 = $this->getMock('\OCP\AppFramework\Middleware', + array('afterException', 'beforeController')); + $m1->expects($this->never()) + ->method('afterException'); + + $m2 = $this->getMock('OCP\AppFramework\Middleware', + array('afterException', 'beforeController')); + $m2->expects($this->once()) + ->method('afterException') + ->will($this->returnValue($response)); + + $this->dispatcher->registerMiddleware($m1); + $this->dispatcher->registerMiddleware($m2); + + $this->dispatcher->beforeController($this->controller, $this->method); + $this->assertEquals($response, $this->dispatcher->afterException($this->controller, $this->method, $this->exception)); + } + + + public function testAfterExceptionShouldThrowAgainWhenNotHandled(){ + $m1 = new TestMiddleware(false); + $m2 = new TestMiddleware(true); + + $this->dispatcher->registerMiddleware($m1); + $this->dispatcher->registerMiddleware($m2); + + $this->setExpectedException('\Exception'); + $this->dispatcher->beforeController($this->controller, $this->method); + $this->dispatcher->afterException($this->controller, $this->method, $this->exception); + } + + + public function testBeforeControllerCorrectArguments(){ + $m1 = $this->getMiddleware(); + $this->dispatcher->beforeController($this->controller, $this->method); + + $this->assertEquals($this->controller, $m1->controller); + $this->assertEquals($this->method, $m1->methodName); + } + + + public function testAfterControllerCorrectArguments(){ + $m1 = $this->getMiddleware(); + + $this->dispatcher->afterController($this->controller, $this->method, $this->response); + + $this->assertEquals($this->controller, $m1->controller); + $this->assertEquals($this->method, $m1->methodName); + $this->assertEquals($this->response, $m1->response); + } + + + public function testAfterExceptionCorrectArguments(){ + $m1 = $this->getMiddleware(); + + $this->setExpectedException('\Exception'); + + $this->dispatcher->beforeController($this->controller, $this->method); + $this->dispatcher->afterException($this->controller, $this->method, $this->exception); + + $this->assertEquals($this->controller, $m1->controller); + $this->assertEquals($this->method, $m1->methodName); + $this->assertEquals($this->exception, $m1->exception); + } + + + public function testBeforeOutputCorrectArguments(){ + $m1 = $this->getMiddleware(); + + $this->dispatcher->beforeOutput($this->controller, $this->method, $this->out); + + $this->assertEquals($this->controller, $m1->controller); + $this->assertEquals($this->method, $m1->methodName); + $this->assertEquals($this->out, $m1->output); + } + + + public function testBeforeControllerOrder(){ + $m1 = $this->getMiddleware(); + $m2 = $this->getMiddleware(); + + $this->dispatcher->beforeController($this->controller, $this->method); + + $this->assertEquals(1, $m1->beforeControllerOrder); + $this->assertEquals(2, $m2->beforeControllerOrder); + } + + public function testAfterControllerOrder(){ + $m1 = $this->getMiddleware(); + $m2 = $this->getMiddleware(); + + $this->dispatcher->afterController($this->controller, $this->method, $this->response); + + $this->assertEquals(2, $m1->afterControllerOrder); + $this->assertEquals(1, $m2->afterControllerOrder); + } + + + public function testAfterExceptionOrder(){ + $m1 = $this->getMiddleware(); + $m2 = $this->getMiddleware(); + + $this->setExpectedException('\Exception'); + $this->dispatcher->beforeController($this->controller, $this->method); + $this->dispatcher->afterException($this->controller, $this->method, $this->exception); + + $this->assertEquals(1, $m1->afterExceptionOrder); + $this->assertEquals(1, $m2->afterExceptionOrder); + } + + + public function testBeforeOutputOrder(){ + $m1 = $this->getMiddleware(); + $m2 = $this->getMiddleware(); + + $this->dispatcher->beforeOutput($this->controller, $this->method, $this->out); + + $this->assertEquals(2, $m1->beforeOutputOrder); + $this->assertEquals(1, $m2->beforeOutputOrder); + } + + + public function testExceptionShouldRunAfterExceptionOfOnlyPreviouslyExecutedMiddlewares(){ + $m1 = $this->getMiddleware(); + $m2 = $this->getMiddleware(true); + $m3 = $this->getMock('\OCP\AppFramework\Middleware'); + $m3->expects($this->never()) + ->method('afterException'); + $m3->expects($this->never()) + ->method('beforeController'); + $m3->expects($this->never()) + ->method('afterController'); + + $this->dispatcher->registerMiddleware($m3); + + $this->dispatcher->beforeOutput($this->controller, $this->method, $this->out); + + $this->assertEquals(2, $m1->beforeOutputOrder); + $this->assertEquals(1, $m2->beforeOutputOrder); + } +} diff --git a/tests/lib/AppFramework/Middleware/MiddlewareTest.php b/tests/lib/AppFramework/Middleware/MiddlewareTest.php new file mode 100644 index 00000000000..013403a9a4a --- /dev/null +++ b/tests/lib/AppFramework/Middleware/MiddlewareTest.php @@ -0,0 +1,99 @@ +<?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\Middleware; + +use OC\AppFramework\Http\Request; +use OCP\AppFramework\Middleware; +use OCP\AppFramework\Http\Response; + +class ChildMiddleware extends Middleware {}; + + +class MiddlewareTest extends \Test\TestCase { + + /** + * @var Middleware + */ + private $middleware; + private $controller; + private $exception; + private $api; + /** @var Response */ + private $response; + + protected function setUp(){ + parent::setUp(); + + $this->middleware = new ChildMiddleware(); + + $this->api = $this->getMockBuilder( + 'OC\AppFramework\DependencyInjection\DIContainer') + ->disableOriginalConstructor() + ->getMock(); + + $this->controller = $this->getMock( + 'OCP\AppFramework\Controller', + [], + [ + $this->api, + new Request( + [], + $this->getMock('\OCP\Security\ISecureRandom'), + $this->getMock('\OCP\IConfig') + ) + ] + ); + $this->exception = new \Exception(); + $this->response = $this->getMock('OCP\AppFramework\Http\Response'); + } + + + public function testBeforeController() { + $this->middleware->beforeController($this->controller, null); + $this->assertNull(null); + } + + + public function testAfterExceptionRaiseAgainWhenUnhandled() { + $this->setExpectedException('Exception'); + $afterEx = $this->middleware->afterException($this->controller, null, $this->exception); + } + + + public function testAfterControllerReturnResponseWhenUnhandled() { + $response = $this->middleware->afterController($this->controller, null, $this->response); + + $this->assertEquals($this->response, $response); + } + + + public function testBeforeOutputReturnOutputhenUnhandled() { + $output = $this->middleware->beforeOutput($this->controller, null, 'test'); + + $this->assertEquals('test', $output); + } + + +} diff --git a/tests/lib/AppFramework/Middleware/Security/CORSMiddlewareTest.php b/tests/lib/AppFramework/Middleware/Security/CORSMiddlewareTest.php new file mode 100644 index 00000000000..8e53c9202cf --- /dev/null +++ b/tests/lib/AppFramework/Middleware/Security/CORSMiddlewareTest.php @@ -0,0 +1,236 @@ +<?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 + */ + + +namespace Test\AppFramework\Middleware\Security; + +use OC\AppFramework\Http\Request; +use OC\AppFramework\Middleware\Security\CORSMiddleware; +use OC\AppFramework\Utility\ControllerMethodReflector; +use OC\AppFramework\Middleware\Security\Exceptions\SecurityException; +use OCP\AppFramework\Http; +use OCP\AppFramework\Http\JSONResponse; +use OCP\AppFramework\Http\Response; + + +class CORSMiddlewareTest extends \Test\TestCase { + + private $reflector; + private $session; + + protected function setUp() { + parent::setUp(); + $this->reflector = new ControllerMethodReflector(); + $this->session = $this->getMock('\OCP\IUserSession'); + } + + /** + * @CORS + */ + public function testSetCORSAPIHeader() { + $request = new Request( + [ + 'server' => [ + 'HTTP_ORIGIN' => 'test' + ] + ], + $this->getMock('\OCP\Security\ISecureRandom'), + $this->getMock('\OCP\IConfig') + ); + $this->reflector->reflect($this, __FUNCTION__); + $middleware = new CORSMiddleware($request, $this->reflector, $this->session); + + $response = $middleware->afterController($this, __FUNCTION__, new Response()); + $headers = $response->getHeaders(); + $this->assertEquals('test', $headers['Access-Control-Allow-Origin']); + } + + + public function testNoAnnotationNoCORSHEADER() { + $request = new Request( + [ + 'server' => [ + 'HTTP_ORIGIN' => 'test' + ] + ], + $this->getMock('\OCP\Security\ISecureRandom'), + $this->getMock('\OCP\IConfig') + ); + $middleware = new CORSMiddleware($request, $this->reflector, $this->session); + + $response = $middleware->afterController($this, __FUNCTION__, new Response()); + $headers = $response->getHeaders(); + $this->assertFalse(array_key_exists('Access-Control-Allow-Origin', $headers)); + } + + + /** + * @CORS + */ + public function testNoOriginHeaderNoCORSHEADER() { + $request = new Request( + [], + $this->getMock('\OCP\Security\ISecureRandom'), + $this->getMock('\OCP\IConfig') + ); + $this->reflector->reflect($this, __FUNCTION__); + $middleware = new CORSMiddleware($request, $this->reflector, $this->session); + + $response = $middleware->afterController($this, __FUNCTION__, new Response()); + $headers = $response->getHeaders(); + $this->assertFalse(array_key_exists('Access-Control-Allow-Origin', $headers)); + } + + + /** + * @CORS + * @expectedException \OC\AppFramework\Middleware\Security\Exceptions\SecurityException + */ + public function testCorsIgnoredIfWithCredentialsHeaderPresent() { + $request = new Request( + [ + 'server' => [ + 'HTTP_ORIGIN' => 'test' + ] + ], + $this->getMock('\OCP\Security\ISecureRandom'), + $this->getMock('\OCP\IConfig') + ); + $this->reflector->reflect($this, __FUNCTION__); + $middleware = new CORSMiddleware($request, $this->reflector, $this->session); + + $response = new Response(); + $response->addHeader('AcCess-control-Allow-Credentials ', 'TRUE'); + $middleware->afterController($this, __FUNCTION__, $response); + } + + /** + * @CORS + * @PublicPage + */ + public function testNoCORSShouldAllowCookieAuth() { + $request = new Request( + [], + $this->getMock('\OCP\Security\ISecureRandom'), + $this->getMock('\OCP\IConfig') + ); + $this->reflector->reflect($this, __FUNCTION__); + $middleware = new CORSMiddleware($request, $this->reflector, $this->session); + $this->session->expects($this->never()) + ->method('logout'); + $this->session->expects($this->never()) + ->method('login') + ->with($this->equalTo('user'), $this->equalTo('pass')) + ->will($this->returnValue(true)); + $this->reflector->reflect($this, __FUNCTION__); + + $middleware->beforeController($this, __FUNCTION__, new Response()); + } + + /** + * @CORS + */ + public function testCORSShouldRelogin() { + $request = new Request( + ['server' => [ + 'PHP_AUTH_USER' => 'user', + 'PHP_AUTH_PW' => 'pass' + ]], + $this->getMock('\OCP\Security\ISecureRandom'), + $this->getMock('\OCP\IConfig') + ); + $this->session->expects($this->once()) + ->method('logout'); + $this->session->expects($this->once()) + ->method('login') + ->with($this->equalTo('user'), $this->equalTo('pass')) + ->will($this->returnValue(true)); + $this->reflector->reflect($this, __FUNCTION__); + $middleware = new CORSMiddleware($request, $this->reflector, $this->session); + + $middleware->beforeController($this, __FUNCTION__, new Response()); + } + + /** + * @CORS + * @expectedException \OC\AppFramework\Middleware\Security\Exceptions\SecurityException + */ + public function testCORSShouldNotAllowCookieAuth() { + $request = new Request( + ['server' => [ + 'PHP_AUTH_USER' => 'user', + 'PHP_AUTH_PW' => 'pass' + ]], + $this->getMock('\OCP\Security\ISecureRandom'), + $this->getMock('\OCP\IConfig') + ); + $this->session->expects($this->once()) + ->method('logout'); + $this->session->expects($this->once()) + ->method('login') + ->with($this->equalTo('user'), $this->equalTo('pass')) + ->will($this->returnValue(false)); + $this->reflector->reflect($this, __FUNCTION__); + $middleware = new CORSMiddleware($request, $this->reflector, $this->session); + + $middleware->beforeController($this, __FUNCTION__, new Response()); + } + + public function testAfterExceptionWithSecurityExceptionNoStatus() { + $request = new Request( + ['server' => [ + 'PHP_AUTH_USER' => 'user', + 'PHP_AUTH_PW' => 'pass' + ]], + $this->getMock('\OCP\Security\ISecureRandom'), + $this->getMock('\OCP\IConfig') + ); + $middleware = new CORSMiddleware($request, $this->reflector, $this->session); + $response = $middleware->afterException($this, __FUNCTION__, new SecurityException('A security exception')); + + $expected = new JSONResponse(['message' => 'A security exception'], 500); + $this->assertEquals($expected, $response); + } + + public function testAfterExceptionWithSecurityExceptionWithStatus() { + $request = new Request( + ['server' => [ + 'PHP_AUTH_USER' => 'user', + 'PHP_AUTH_PW' => 'pass' + ]], + $this->getMock('\OCP\Security\ISecureRandom'), + $this->getMock('\OCP\IConfig') + ); + $middleware = new CORSMiddleware($request, $this->reflector, $this->session); + $response = $middleware->afterException($this, __FUNCTION__, new SecurityException('A security exception', 501)); + + $expected = new JSONResponse(['message' => 'A security exception'], 501); + $this->assertEquals($expected, $response); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage A regular exception + */ + public function testAfterExceptionWithRegularException() { + $request = new Request( + ['server' => [ + 'PHP_AUTH_USER' => 'user', + 'PHP_AUTH_PW' => 'pass' + ]], + $this->getMock('\OCP\Security\ISecureRandom'), + $this->getMock('\OCP\IConfig') + ); + $middleware = new CORSMiddleware($request, $this->reflector, $this->session); + $middleware->afterException($this, __FUNCTION__, new \Exception('A regular exception')); + } + +} diff --git a/tests/lib/AppFramework/Middleware/Security/SecurityMiddlewareTest.php b/tests/lib/AppFramework/Middleware/Security/SecurityMiddlewareTest.php new file mode 100644 index 00000000000..8cdba76d835 --- /dev/null +++ b/tests/lib/AppFramework/Middleware/Security/SecurityMiddlewareTest.php @@ -0,0 +1,453 @@ +<?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/> + * + */ + + + +namespace Test\AppFramework\Middleware\Security; + +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\NotAdminException; +use OC\AppFramework\Middleware\Security\Exceptions\NotLoggedInException; +use OC\AppFramework\Middleware\Security\Exceptions\SecurityException; +use OC\AppFramework\Middleware\Security\SecurityMiddleware; +use OC\AppFramework\Utility\ControllerMethodReflector; +use OC\Security\CSP\ContentSecurityPolicy; +use OCP\AppFramework\Http\RedirectResponse; +use OCP\AppFramework\Http\JSONResponse; +use OCP\AppFramework\Http\TemplateResponse; + + +class SecurityMiddlewareTest extends \Test\TestCase { + + private $middleware; + private $controller; + private $secException; + private $secAjaxException; + private $request; + private $reader; + private $logger; + private $navigationManager; + private $urlGenerator; + private $contentSecurityPolicyManager; + + protected function setUp() { + parent::setUp(); + + $this->controller = $this->getMockBuilder('OCP\AppFramework\Controller') + ->disableOriginalConstructor() + ->getMock(); + $this->reader = new ControllerMethodReflector(); + $this->logger = $this->getMockBuilder( + 'OCP\ILogger') + ->disableOriginalConstructor() + ->getMock(); + $this->navigationManager = $this->getMockBuilder( + 'OCP\INavigationManager') + ->disableOriginalConstructor() + ->getMock(); + $this->urlGenerator = $this->getMockBuilder( + 'OCP\IURLGenerator') + ->disableOriginalConstructor() + ->getMock(); + $this->request = $this->getMockBuilder( + 'OCP\IRequest') + ->disableOriginalConstructor() + ->getMock(); + $this->contentSecurityPolicyManager = $this->getMockBuilder( + 'OC\Security\CSP\ContentSecurityPolicyManager') + ->disableOriginalConstructor() + ->getMock(); + $this->middleware = $this->getMiddleware(true, true); + $this->secException = new SecurityException('hey', false); + $this->secAjaxException = new SecurityException('hey', true); + } + + /** + * @param bool $isLoggedIn + * @param bool $isAdminUser + * @return SecurityMiddleware + */ + private function getMiddleware($isLoggedIn, $isAdminUser) { + return new SecurityMiddleware( + $this->request, + $this->reader, + $this->navigationManager, + $this->urlGenerator, + $this->logger, + 'files', + $isLoggedIn, + $isAdminUser, + $this->contentSecurityPolicyManager + ); + } + + + /** + * @PublicPage + * @NoCSRFRequired + */ + public function testSetNavigationEntry(){ + $this->navigationManager->expects($this->once()) + ->method('setActiveEntry') + ->with($this->equalTo('files')); + + $this->reader->reflect(__CLASS__, __FUNCTION__); + $this->middleware->beforeController(__CLASS__, __FUNCTION__); + } + + + /** + * @param string $method + * @param string $test + */ + private function ajaxExceptionStatus($method, $test, $status) { + $isLoggedIn = false; + $isAdminUser = false; + + // isAdminUser requires isLoggedIn call to return true + if ($test === 'isAdminUser') { + $isLoggedIn = true; + } + + $sec = $this->getMiddleware($isLoggedIn, $isAdminUser); + + try { + $this->reader->reflect(__CLASS__, $method); + $sec->beforeController(__CLASS__, $method); + } catch (SecurityException $ex){ + $this->assertEquals($status, $ex->getCode()); + } + + // add assertion if everything should work fine otherwise phpunit will + // complain + if ($status === 0) { + $this->assertTrue(true); + } + } + + public function testAjaxStatusLoggedInCheck() { + $this->ajaxExceptionStatus( + __FUNCTION__, + 'isLoggedIn', + Http::STATUS_UNAUTHORIZED + ); + } + + /** + * @NoCSRFRequired + */ + public function testAjaxNotAdminCheck() { + $this->ajaxExceptionStatus( + __FUNCTION__, + 'isAdminUser', + Http::STATUS_FORBIDDEN + ); + } + + /** + * @PublicPage + */ + public function testAjaxStatusCSRFCheck() { + $this->ajaxExceptionStatus( + __FUNCTION__, + 'passesCSRFCheck', + Http::STATUS_PRECONDITION_FAILED + ); + } + + /** + * @PublicPage + * @NoCSRFRequired + */ + public function testAjaxStatusAllGood() { + $this->ajaxExceptionStatus( + __FUNCTION__, + 'isLoggedIn', + 0 + ); + $this->ajaxExceptionStatus( + __FUNCTION__, + 'isAdminUser', + 0 + ); + $this->ajaxExceptionStatus( + __FUNCTION__, + 'isSubAdminUser', + 0 + ); + $this->ajaxExceptionStatus( + __FUNCTION__, + 'passesCSRFCheck', + 0 + ); + } + + + /** + * @PublicPage + * @NoCSRFRequired + */ + public function testNoChecks(){ + $this->request->expects($this->never()) + ->method('passesCSRFCheck') + ->will($this->returnValue(false)); + + $sec = $this->getMiddleware(false, false); + + $this->reader->reflect(__CLASS__, __FUNCTION__); + $sec->beforeController(__CLASS__, __FUNCTION__); + } + + + /** + * @param string $method + * @param string $expects + */ + private function securityCheck($method, $expects, $shouldFail=false){ + // admin check requires login + if ($expects === 'isAdminUser') { + $isLoggedIn = true; + $isAdminUser = !$shouldFail; + } else { + $isLoggedIn = !$shouldFail; + $isAdminUser = false; + } + + $sec = $this->getMiddleware($isLoggedIn, $isAdminUser); + + if($shouldFail) { + $this->setExpectedException('\OC\AppFramework\Middleware\Security\Exceptions\SecurityException'); + } else { + $this->assertTrue(true); + } + + $this->reader->reflect(__CLASS__, $method); + $sec->beforeController(__CLASS__, $method); + } + + + /** + * @PublicPage + * @expectedException \OC\AppFramework\Middleware\Security\Exceptions\CrossSiteRequestForgeryException + */ + public function testCsrfCheck(){ + $this->request->expects($this->once()) + ->method('passesCSRFCheck') + ->will($this->returnValue(false)); + + $this->reader->reflect(__CLASS__, __FUNCTION__); + $this->middleware->beforeController(__CLASS__, __FUNCTION__); + } + + + /** + * @PublicPage + * @NoCSRFRequired + */ + public function testNoCsrfCheck(){ + $this->request->expects($this->never()) + ->method('passesCSRFCheck') + ->will($this->returnValue(false)); + + $this->reader->reflect(__CLASS__, __FUNCTION__); + $this->middleware->beforeController(__CLASS__, __FUNCTION__); + } + + + /** + * @PublicPage + */ + public function testFailCsrfCheck(){ + $this->request->expects($this->once()) + ->method('passesCSRFCheck') + ->will($this->returnValue(true)); + + $this->reader->reflect(__CLASS__, __FUNCTION__); + $this->middleware->beforeController(__CLASS__, __FUNCTION__); + } + + + /** + * @NoCSRFRequired + * @NoAdminRequired + */ + public function testLoggedInCheck(){ + $this->securityCheck(__FUNCTION__, 'isLoggedIn'); + } + + + /** + * @NoCSRFRequired + * @NoAdminRequired + */ + public function testFailLoggedInCheck(){ + $this->securityCheck(__FUNCTION__, 'isLoggedIn', true); + } + + + /** + * @NoCSRFRequired + */ + public function testIsAdminCheck(){ + $this->securityCheck(__FUNCTION__, 'isAdminUser'); + } + + + /** + * @NoCSRFRequired + */ + public function testFailIsAdminCheck(){ + $this->securityCheck(__FUNCTION__, 'isAdminUser', true); + } + + + public function testAfterExceptionNotCaughtThrowsItAgain(){ + $ex = new \Exception(); + $this->setExpectedException('\Exception'); + $this->middleware->afterException($this->controller, 'test', $ex); + } + + public function testAfterExceptionReturnsRedirectForNotLoggedInUser() { + $this->request = new Request( + [ + 'server' => + [ + 'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', + 'REQUEST_URI' => 'owncloud/index.php/apps/specialapp' + ] + ], + $this->getMock('\OCP\Security\ISecureRandom'), + $this->getMock('\OCP\IConfig') + ); + $this->middleware = $this->getMiddleware(false, false); + $this->urlGenerator + ->expects($this->once()) + ->method('linkToRoute') + ->with( + 'core.login.showLoginForm', + [ + 'redirect_url' => 'owncloud%2Findex.php%2Fapps%2Fspecialapp', + ] + ) + ->will($this->returnValue('http://localhost/index.php/login?redirect_url=owncloud%2Findex.php%2Fapps%2Fspecialapp')); + $this->logger + ->expects($this->once()) + ->method('debug') + ->with('Current user is not logged in'); + $response = $this->middleware->afterException( + $this->controller, + 'test', + new NotLoggedInException() + ); + + $expected = new RedirectResponse('http://localhost/index.php/login?redirect_url=owncloud%2Findex.php%2Fapps%2Fspecialapp'); + $this->assertEquals($expected , $response); + } + + /** + * @return array + */ + public function exceptionProvider() { + return [ + [ + new AppNotEnabledException(), + ], + [ + new CrossSiteRequestForgeryException(), + ], + [ + new NotAdminException(), + ], + ]; + } + + /** + * @dataProvider exceptionProvider + * @param SecurityException $exception + */ + public function testAfterExceptionReturnsTemplateResponse(SecurityException $exception) { + $this->request = new Request( + [ + 'server' => + [ + 'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', + 'REQUEST_URI' => 'owncloud/index.php/apps/specialapp' + ] + ], + $this->getMock('\OCP\Security\ISecureRandom'), + $this->getMock('\OCP\IConfig') + ); + $this->middleware = $this->getMiddleware(false, false); + $this->logger + ->expects($this->once()) + ->method('debug') + ->with($exception->getMessage()); + $response = $this->middleware->afterException( + $this->controller, + 'test', + $exception + ); + + $expected = new TemplateResponse('core', '403', ['file' => $exception->getMessage()], 'guest'); + $expected->setStatus($exception->getCode()); + $this->assertEquals($expected , $response); + } + + + public function testAfterAjaxExceptionReturnsJSONError(){ + $response = $this->middleware->afterException($this->controller, 'test', + $this->secAjaxException); + + $this->assertTrue($response instanceof JSONResponse); + } + + public function testAfterController() { + $response = $this->getMockBuilder('\OCP\AppFramework\Http\Response')->disableOriginalConstructor()->getMock(); + $defaultPolicy = new ContentSecurityPolicy(); + $defaultPolicy->addAllowedImageDomain('defaultpolicy'); + $currentPolicy = new ContentSecurityPolicy(); + $currentPolicy->addAllowedConnectDomain('currentPolicy'); + $mergedPolicy = new ContentSecurityPolicy(); + $mergedPolicy->addAllowedMediaDomain('mergedPolicy'); + $response + ->expects($this->exactly(2)) + ->method('getContentSecurityPolicy') + ->willReturn($currentPolicy); + $this->contentSecurityPolicyManager + ->expects($this->once()) + ->method('getDefaultPolicy') + ->willReturn($defaultPolicy); + $this->contentSecurityPolicyManager + ->expects($this->once()) + ->method('mergePolicies') + ->with($defaultPolicy, $currentPolicy) + ->willReturn($mergedPolicy); + $response->expects($this->once()) + ->method('setContentSecurityPolicy') + ->with($mergedPolicy); + + $this->middleware->afterController($this->controller, 'test', $response); + } +} diff --git a/tests/lib/AppFramework/Middleware/SessionMiddlewareTest.php b/tests/lib/AppFramework/Middleware/SessionMiddlewareTest.php new file mode 100644 index 00000000000..17fcc1904c1 --- /dev/null +++ b/tests/lib/AppFramework/Middleware/SessionMiddlewareTest.php @@ -0,0 +1,95 @@ +<?php +/** + * 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 + */ + + +namespace Test\AppFramework\Middleware; + +use OC\AppFramework\Http\Request; +use OC\AppFramework\Middleware\SessionMiddleware; +use OC\AppFramework\Utility\ControllerMethodReflector; +use OCP\AppFramework\Http\Response; + + +class SessionMiddlewareTest extends \Test\TestCase { + + /** + * @var ControllerMethodReflector + */ + private $reflector; + + /** + * @var Request + */ + private $request; + + protected function setUp() { + parent::setUp(); + + $this->request = new Request( + [], + $this->getMockBuilder('\OCP\Security\ISecureRandom')->getMock(), + $this->getMock('\OCP\IConfig') + ); + $this->reflector = new ControllerMethodReflector(); + } + + /** + * @UseSession + */ + public function testSessionNotClosedOnBeforeController() { + $session = $this->getSessionMock(0); + + $this->reflector->reflect($this, __FUNCTION__); + $middleware = new SessionMiddleware($this->request, $this->reflector, $session); + $middleware->beforeController($this, __FUNCTION__); + } + + /** + * @UseSession + */ + public function testSessionClosedOnAfterController() { + $session = $this->getSessionMock(1); + + $this->reflector->reflect($this, __FUNCTION__); + $middleware = new SessionMiddleware($this->request, $this->reflector, $session); + $middleware->afterController($this, __FUNCTION__, new Response()); + } + + public function testSessionClosedOnBeforeController() { + $session = $this->getSessionMock(1); + + $this->reflector->reflect($this, __FUNCTION__); + $middleware = new SessionMiddleware($this->request, $this->reflector, $session); + $middleware->beforeController($this, __FUNCTION__); + } + + public function testSessionNotClosedOnAfterController() { + $session = $this->getSessionMock(0); + + $this->reflector->reflect($this, __FUNCTION__); + $middleware = new SessionMiddleware($this->request, $this->reflector, $session); + $middleware->afterController($this, __FUNCTION__, new Response()); + } + + /** + * @return mixed + */ + private function getSessionMock($expectedCloseCount) { + $session = $this->getMockBuilder('\OC\Session\Memory') + ->disableOriginalConstructor() + ->getMock(); + + $session->expects($this->exactly($expectedCloseCount)) + ->method('close'); + return $session; + } + +} |