diff options
author | Lukas Reschke <lukas@statuscode.ch> | 2016-07-20 21:35:02 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-07-20 21:35:02 +0200 |
commit | 020a2a6958e48f7a3a29daa2235f6729980850af (patch) | |
tree | fac1cf75e60a7d46c978b7f9aebd811c932da7ab /tests | |
parent | a17ba2f4889c92e7113606e17cc6b9f66512264f (diff) | |
parent | a299fa38a9172f16e4bc48d4bd4f9807cec2f737 (diff) | |
download | nextcloud-server-020a2a6958e48f7a3a29daa2235f6729980850af.tar.gz nextcloud-server-020a2a6958e48f7a3a29daa2235f6729980850af.zip |
Merge pull request #476 from nextcloud/port-same-site-cookies
[master] Port Same-Site Cookies to master
Diffstat (limited to 'tests')
-rw-r--r-- | tests/lib/AppFramework/Http/RequestTest.php | 307 | ||||
-rw-r--r-- | tests/lib/AppFramework/Middleware/Security/SecurityMiddlewareTest.php | 218 |
2 files changed, 431 insertions, 94 deletions
diff --git a/tests/lib/AppFramework/Http/RequestTest.php b/tests/lib/AppFramework/Http/RequestTest.php index ddc2403d866..8df81afeb3b 100644 --- a/tests/lib/AppFramework/Http/RequestTest.php +++ b/tests/lib/AppFramework/Http/RequestTest.php @@ -1,7 +1,7 @@ <?php /** * @copyright 2013 Thomas Tanghus (thomas@tanghus.net) - * @copyright 2015 Lukas Reschke lukas@owncloud.com + * @copyright 2016 Lukas Reschke lukas@owncloud.com * * This file is licensed under the Affero General Public License version 3 or * later. @@ -740,15 +740,15 @@ class RequestTest extends \Test\TestCase { */ public function testUserAgent($testAgent, $userAgent, $matches) { $request = new Request( - [ - 'server' => [ - 'HTTP_USER_AGENT' => $testAgent, - ] - ], - $this->secureRandom, - $this->config, - $this->csrfTokenManager, - $this->stream + [ + 'server' => [ + 'HTTP_USER_AGENT' => $testAgent, + ] + ], + $this->secureRandom, + $this->config, + $this->csrfTokenManager, + $this->stream ); $this->assertSame($matches, $request->isUserAgent($userAgent)); @@ -762,11 +762,11 @@ class RequestTest extends \Test\TestCase { */ public function testUndefinedUserAgent($testAgent, $userAgent, $matches) { $request = new Request( - [], - $this->secureRandom, - $this->config, - $this->csrfTokenManager, - $this->stream + [], + $this->secureRandom, + $this->config, + $this->csrfTokenManager, + $this->stream ); $this->assertFalse($request->isUserAgent($userAgent)); @@ -1322,6 +1322,10 @@ class RequestTest extends \Test\TestCase { 'get' => [ 'requesttoken' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds', ], + 'cookies' => [ + 'nc_sameSiteCookiestrict' => 'true', + 'nc_sameSiteCookielax' => 'true', + ], ], $this->secureRandom, $this->config, @@ -1348,6 +1352,10 @@ class RequestTest extends \Test\TestCase { 'post' => [ 'requesttoken' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds', ], + 'cookies' => [ + 'nc_sameSiteCookiestrict' => 'true', + 'nc_sameSiteCookielax' => 'true', + ], ], $this->secureRandom, $this->config, @@ -1357,10 +1365,10 @@ class RequestTest extends \Test\TestCase { ->getMock(); $token = new CsrfToken('AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds'); $this->csrfTokenManager - ->expects($this->once()) - ->method('isTokenValid') - ->with($token) - ->willReturn(true); + ->expects($this->once()) + ->method('isTokenValid') + ->with($token) + ->willReturn(true); $this->assertTrue($request->passesCSRFCheck()); } @@ -1374,6 +1382,10 @@ class RequestTest extends \Test\TestCase { 'server' => [ 'HTTP_REQUESTTOKEN' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds', ], + 'cookies' => [ + 'nc_sameSiteCookiestrict' => 'true', + 'nc_sameSiteCookielax' => 'true', + ], ], $this->secureRandom, $this->config, @@ -1383,14 +1395,254 @@ class RequestTest extends \Test\TestCase { ->getMock(); $token = new CsrfToken('AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds'); $this->csrfTokenManager - ->expects($this->once()) - ->method('isTokenValid') - ->with($token) - ->willReturn(true); + ->expects($this->once()) + ->method('isTokenValid') + ->with($token) + ->willReturn(true); + + $this->assertTrue($request->passesCSRFCheck()); + } + + public function testPassesCSRFCheckWithGetAndWithoutCookies() { + /** @var Request $request */ + $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') + ->setMethods(['getScriptName']) + ->setConstructorArgs([ + [ + 'get' => [ + 'requesttoken' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds', + ], + ], + $this->secureRandom, + $this->config, + $this->csrfTokenManager, + $this->stream + ]) + ->getMock(); + $this->csrfTokenManager + ->expects($this->once()) + ->method('isTokenValid') + ->willReturn(true); + + $this->assertTrue($request->passesCSRFCheck()); + } + + public function testPassesCSRFCheckWithPostAndWithoutCookies() { + /** @var Request $request */ + $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') + ->setMethods(['getScriptName']) + ->setConstructorArgs([ + [ + 'post' => [ + 'requesttoken' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds', + ], + ], + $this->secureRandom, + $this->config, + $this->csrfTokenManager, + $this->stream + ]) + ->getMock(); + $this->csrfTokenManager + ->expects($this->once()) + ->method('isTokenValid') + ->willReturn(true); $this->assertTrue($request->passesCSRFCheck()); } + public function testPassesCSRFCheckWithHeaderAndWithoutCookies() { + /** @var Request $request */ + $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') + ->setMethods(['getScriptName']) + ->setConstructorArgs([ + [ + 'server' => [ + 'HTTP_REQUESTTOKEN' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds', + ], + ], + $this->secureRandom, + $this->config, + $this->csrfTokenManager, + $this->stream + ]) + ->getMock(); + $this->csrfTokenManager + ->expects($this->once()) + ->method('isTokenValid') + ->willReturn(true); + + $this->assertTrue($request->passesCSRFCheck()); + } + + public function testFailsCSRFCheckWithHeaderAndNotAllChecksPassing() { + /** @var Request $request */ + $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') + ->setMethods(['getScriptName']) + ->setConstructorArgs([ + [ + 'server' => [ + 'HTTP_REQUESTTOKEN' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds', + ], + 'cookies' => [ + 'nc_sameSiteCookiestrict' => 'true', + ], + ], + $this->secureRandom, + $this->config, + $this->csrfTokenManager, + $this->stream + ]) + ->getMock(); + $this->csrfTokenManager + ->expects($this->never()) + ->method('isTokenValid'); + + $this->assertFalse($request->passesCSRFCheck()); + } + + public function testPassesStrictCookieCheckWithAllCookies() { + /** @var Request $request */ + $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') + ->setMethods(['getScriptName']) + ->setConstructorArgs([ + [ + 'server' => [ + 'HTTP_REQUESTTOKEN' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds', + ], + 'cookies' => [ + 'nc_sameSiteCookiestrict' => 'true', + 'nc_sameSiteCookielax' => 'true', + ], + ], + $this->secureRandom, + $this->config, + $this->csrfTokenManager, + $this->stream + ]) + ->getMock(); + + $this->assertTrue($request->passesStrictCookieCheck()); + } + + public function testFailsSRFCheckWithPostAndWithCookies() { + /** @var Request $request */ + $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') + ->setMethods(['getScriptName']) + ->setConstructorArgs([ + [ + 'post' => [ + 'requesttoken' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds', + ], + 'cookies' => [ + 'foo' => 'bar', + ], + ], + $this->secureRandom, + $this->config, + $this->csrfTokenManager, + $this->stream + ]) + ->getMock(); + $this->csrfTokenManager + ->expects($this->never()) + ->method('isTokenValid'); + + $this->assertFalse($request->passesCSRFCheck()); + } + + public function testFailStrictCookieCheckWithOnlyLaxCookie() { + /** @var Request $request */ + $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') + ->setMethods(['getScriptName']) + ->setConstructorArgs([ + [ + 'server' => [ + 'HTTP_REQUESTTOKEN' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds', + ], + 'cookies' => [ + 'nc_sameSiteCookielax' => 'true', + ], + ], + $this->secureRandom, + $this->config, + $this->csrfTokenManager, + $this->stream + ]) + ->getMock(); + + $this->assertFalse($request->passesStrictCookieCheck()); + } + + public function testFailStrictCookieCheckWithOnlyStrictCookie() { + /** @var Request $request */ + $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') + ->setMethods(['getScriptName']) + ->setConstructorArgs([ + [ + 'server' => [ + 'HTTP_REQUESTTOKEN' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds', + ], + 'cookies' => [ + 'nc_sameSiteCookiestrict' => 'true', + ], + ], + $this->secureRandom, + $this->config, + $this->csrfTokenManager, + $this->stream + ]) + ->getMock(); + + $this->assertFalse($request->passesStrictCookieCheck()); + } + + public function testPassesLaxCookieCheck() { + /** @var Request $request */ + $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') + ->setMethods(['getScriptName']) + ->setConstructorArgs([ + [ + 'server' => [ + 'HTTP_REQUESTTOKEN' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds', + ], + 'cookies' => [ + 'nc_sameSiteCookielax' => 'true', + ], + ], + $this->secureRandom, + $this->config, + $this->csrfTokenManager, + $this->stream + ]) + ->getMock(); + + $this->assertTrue($request->passesLaxCookieCheck()); + } + + public function testFailsLaxCookieCheckWithOnlyStrictCookie() { + /** @var Request $request */ + $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') + ->setMethods(['getScriptName']) + ->setConstructorArgs([ + [ + 'server' => [ + 'HTTP_REQUESTTOKEN' => 'AAAHGxsTCTc3BgMQESAcNR0OAR0=:MyTotalSecretShareds', + ], + 'cookies' => [ + 'nc_sameSiteCookiestrict' => 'true', + ], + ], + $this->secureRandom, + $this->config, + $this->csrfTokenManager, + $this->stream + ]) + ->getMock(); + + $this->assertFalse($request->passesLaxCookieCheck()); + } + /** * @return array */ @@ -1426,10 +1678,10 @@ class RequestTest extends \Test\TestCase { $token = new CsrfToken($invalidToken); $this->csrfTokenManager - ->expects($this->any()) - ->method('isTokenValid') - ->with($token) - ->willReturn(false); + ->expects($this->any()) + ->method('isTokenValid') + ->with($token) + ->willReturn(false); $this->assertFalse($request->passesCSRFCheck()); } @@ -1449,5 +1701,4 @@ class RequestTest extends \Test\TestCase { $this->assertFalse($request->passesCSRFCheck()); } - } diff --git a/tests/lib/AppFramework/Middleware/Security/SecurityMiddlewareTest.php b/tests/lib/AppFramework/Middleware/Security/SecurityMiddlewareTest.php index a4f203bacd7..487b83c0bef 100644 --- a/tests/lib/AppFramework/Middleware/Security/SecurityMiddlewareTest.php +++ b/tests/lib/AppFramework/Middleware/Security/SecurityMiddlewareTest.php @@ -31,6 +31,7 @@ use OC\AppFramework\Middleware\Security\Exceptions\CrossSiteRequestForgeryExcept 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\Exceptions\StrictCookieMissingException; use OC\AppFramework\Middleware\Security\SecurityMiddleware; use OC\AppFramework\Utility\ControllerMethodReflector; use OC\Security\CSP\ContentSecurityPolicy; @@ -57,28 +58,28 @@ class SecurityMiddlewareTest extends \Test\TestCase { $this->controller = $this->getMockBuilder('OCP\AppFramework\Controller') ->disableOriginalConstructor() - ->getMock(); + ->getMock(); $this->reader = new ControllerMethodReflector(); $this->logger = $this->getMockBuilder( - 'OCP\ILogger') - ->disableOriginalConstructor() - ->getMock(); + 'OCP\ILogger') + ->disableOriginalConstructor() + ->getMock(); $this->navigationManager = $this->getMockBuilder( - 'OCP\INavigationManager') - ->disableOriginalConstructor() - ->getMock(); + 'OCP\INavigationManager') + ->disableOriginalConstructor() + ->getMock(); $this->urlGenerator = $this->getMockBuilder( - 'OCP\IURLGenerator') - ->disableOriginalConstructor() - ->getMock(); + 'OCP\IURLGenerator') + ->disableOriginalConstructor() + ->getMock(); $this->request = $this->getMockBuilder( - 'OCP\IRequest') - ->disableOriginalConstructor() - ->getMock(); + 'OCP\IRequest') + ->disableOriginalConstructor() + ->getMock(); $this->contentSecurityPolicyManager = $this->getMockBuilder( - 'OC\Security\CSP\ContentSecurityPolicyManager') - ->disableOriginalConstructor() - ->getMock(); + 'OC\Security\CSP\ContentSecurityPolicyManager') + ->disableOriginalConstructor() + ->getMock(); $this->middleware = $this->getMiddleware(true, true); $this->secException = new SecurityException('hey', false); $this->secAjaxException = new SecurityException('hey', true); @@ -211,8 +212,8 @@ class SecurityMiddlewareTest extends \Test\TestCase { */ public function testNoChecks(){ $this->request->expects($this->never()) - ->method('passesCSRFCheck') - ->will($this->returnValue(false)); + ->method('passesCSRFCheck') + ->will($this->returnValue(false)); $sec = $this->getMiddleware(false, false); @@ -256,7 +257,9 @@ class SecurityMiddlewareTest extends \Test\TestCase { $this->request->expects($this->once()) ->method('passesCSRFCheck') ->will($this->returnValue(false)); - + $this->request->expects($this->once()) + ->method('passesStrictCookieCheck') + ->will($this->returnValue(true)); $this->reader->reflect(__CLASS__, __FUNCTION__); $this->middleware->beforeController(__CLASS__, __FUNCTION__); } @@ -275,19 +278,81 @@ class SecurityMiddlewareTest extends \Test\TestCase { $this->middleware->beforeController(__CLASS__, __FUNCTION__); } + /** + * @PublicPage + */ + public function testPassesCsrfCheck(){ + $this->request->expects($this->once()) + ->method('passesCSRFCheck') + ->will($this->returnValue(true)); + $this->request->expects($this->once()) + ->method('passesStrictCookieCheck') + ->will($this->returnValue(true)); + + $this->reader->reflect(__CLASS__, __FUNCTION__); + $this->middleware->beforeController(__CLASS__, __FUNCTION__); + } /** * @PublicPage + * @expectedException \OC\AppFramework\Middleware\Security\Exceptions\CrossSiteRequestForgeryException */ public function testFailCsrfCheck(){ $this->request->expects($this->once()) ->method('passesCSRFCheck') + ->will($this->returnValue(false)); + $this->request->expects($this->once()) + ->method('passesStrictCookieCheck') ->will($this->returnValue(true)); $this->reader->reflect(__CLASS__, __FUNCTION__); $this->middleware->beforeController(__CLASS__, __FUNCTION__); } + /** + * @PublicPage + * @StrictCookieRequired + * @expectedException \OC\Appframework\Middleware\Security\Exceptions\StrictCookieMissingException + */ + public function testStrictCookieRequiredCheck() { + $this->request->expects($this->never()) + ->method('passesCSRFCheck'); + $this->request->expects($this->once()) + ->method('passesStrictCookieCheck') + ->will($this->returnValue(false)); + + $this->reader->reflect(__CLASS__, __FUNCTION__); + $this->middleware->beforeController(__CLASS__, __FUNCTION__); + } + + + /** + * @PublicPage + * @NoCSRFRequired + */ + public function testNoStrictCookieRequiredCheck() { + $this->request->expects($this->never()) + ->method('passesStrictCookieCheck') + ->will($this->returnValue(false)); + + $this->reader->reflect(__CLASS__, __FUNCTION__); + $this->middleware->beforeController(__CLASS__, __FUNCTION__); + } + + /** + * @PublicPage + * @NoCSRFRequired + * @StrictCookieRequired + */ + public function testPassesStrictCookieRequiredCheck() { + $this->request + ->expects($this->once()) + ->method('passesStrictCookieCheck') + ->willReturn(true); + + $this->reader->reflect(__CLASS__, __FUNCTION__); + $this->middleware->beforeController(__CLASS__, __FUNCTION__); + } /** * @NoCSRFRequired @@ -331,41 +396,64 @@ class SecurityMiddlewareTest extends \Test\TestCase { 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->getMockBuilder('\OCP\Security\ISecureRandom')->getMock(), - $this->getMockBuilder('\OCP\IConfig')->getMock() + [ + 'server' => + [ + 'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', + 'REQUEST_URI' => 'owncloud/index.php/apps/specialapp' + ] + ], + $this->getMockBuilder('\OCP\Security\ISecureRandom')->getMock(), + $this->getMockBuilder('\OCP\IConfig')->getMock() ); $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')); + ->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'); + ->expects($this->once()) + ->method('debug') + ->with('Current user is not logged in'); $response = $this->middleware->afterException( - $this->controller, - 'test', - new NotLoggedInException() + $this->controller, + 'test', + new NotLoggedInException() ); - $expected = new RedirectResponse('http://localhost/index.php/login?redirect_url=owncloud%2Findex.php%2Fapps%2Fspecialapp'); $this->assertEquals($expected , $response); } + public function testAfterExceptionRedirectsToWebRootAfterStrictCookieFail() { + $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->getMockBuilder('\OCP\Security\ISecureRandom')->getMock(), + $this->getMockBuilder('\OCP\IConfig')->getMock() + ); + + $this->middleware = $this->getMiddleware(false, false); + $response = $this->middleware->afterException( + $this->controller, + 'test', + new StrictCookieMissingException() + ); + + $expected = new RedirectResponse(\OC::$WEBROOT); + $this->assertEquals($expected , $response); + } + + /** * @return array */ @@ -389,36 +477,34 @@ class SecurityMiddlewareTest extends \Test\TestCase { */ 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->getMockBuilder('\OCP\Security\ISecureRandom')->getMock(), - $this->getMockBuilder('\OCP\IConfig')->getMock() + [ + 'server' => + [ + 'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', + 'REQUEST_URI' => 'owncloud/index.php/apps/specialapp' + ] + ], + $this->getMockBuilder('\OCP\Security\ISecureRandom')->getMock(), + $this->getMockBuilder('\OCP\IConfig')->getMock() ); $this->middleware = $this->getMiddleware(false, false); $this->logger - ->expects($this->once()) - ->method('debug') - ->with($exception->getMessage()); + ->expects($this->once()) + ->method('debug') + ->with($exception->getMessage()); $response = $this->middleware->afterException( - $this->controller, - 'test', - $exception + $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->secAjaxException); $this->assertTrue($response instanceof JSONResponse); } @@ -440,10 +526,10 @@ class SecurityMiddlewareTest extends \Test\TestCase { ->method('getDefaultPolicy') ->willReturn($defaultPolicy); $this->contentSecurityPolicyManager - ->expects($this->once()) - ->method('mergePolicies') - ->with($defaultPolicy, $currentPolicy) - ->willReturn($mergedPolicy); + ->expects($this->once()) + ->method('mergePolicies') + ->with($defaultPolicy, $currentPolicy) + ->willReturn($mergedPolicy); $response->expects($this->once()) ->method('setContentSecurityPolicy') ->with($mergedPolicy); |