From 886bda5f81d52ba4443094e4c2fffac33c27bc4b Mon Sep 17 00:00:00 2001 From: Lukas Reschke Date: Tue, 10 Feb 2015 13:02:48 +0100 Subject: Refactor OC_Request into TrustedDomainHelper and IRequest This changeset removes the static class `OC_Request` and moves the functions either into `IRequest` which is accessible via `\OC::$server::->getRequest()` or into a separated `TrustedDomainHelper` class for some helper methods which should not be publicly exposed. This changes only internal methods and nothing on the public API. Some public functions in `util.php` have been deprecated though in favour of the new non-static functions. Unfortunately some part of this code uses things like `__DIR__` and thus is not completely unit-testable. Where tests where possible they ahve been added though. Fixes https://github.com/owncloud/core/issues/13976 which was requested in https://github.com/owncloud/core/pull/13973#issuecomment-73492969 --- tests/lib/appframework/http/RequestTest.php | 804 +++++++++++++++++++++++++++- 1 file changed, 784 insertions(+), 20 deletions(-) (limited to 'tests/lib/appframework/http/RequestTest.php') diff --git a/tests/lib/appframework/http/RequestTest.php b/tests/lib/appframework/http/RequestTest.php index eeba64b7f69..3185a0093c4 100644 --- a/tests/lib/appframework/http/RequestTest.php +++ b/tests/lib/appframework/http/RequestTest.php @@ -1,6 +1,8 @@ secureRandom = $this->getMockBuilder('\OCP\Security\ISecureRandom')->getMock(); + $this->config = $this->getMockBuilder('\OCP\IConfig')->getMock(); } protected function tearDown() { @@ -39,7 +50,12 @@ class RequestTest extends \Test\TestCase { 'method' => 'GET', ); - $request = new Request($vars, $this->secureRandom, $this->stream); + $request = new Request( + $vars, + $this->secureRandom, + $this->config, + $this->stream + ); // Countable $this->assertEquals(2, count($request)); @@ -66,7 +82,12 @@ class RequestTest extends \Test\TestCase { 'method' => 'GET' ); - $request = new Request($vars, $this->secureRandom, $this->stream); + $request = new Request( + $vars, + $this->secureRandom, + $this->config, + $this->stream + ); $this->assertEquals(3, count($request)); $this->assertEquals('Janey', $request->{'nickname'}); @@ -75,7 +96,7 @@ class RequestTest extends \Test\TestCase { /** - * @expectedException RuntimeException + * @expectedException \RuntimeException */ public function testImmutableArrayAccess() { $vars = array( @@ -83,12 +104,18 @@ class RequestTest extends \Test\TestCase { 'method' => 'GET' ); - $request = new Request($vars, $this->secureRandom, $this->stream); + $request = new Request( + $vars, + $this->secureRandom, + $this->config, + $this->stream + ); + $request['nickname'] = 'Janey'; } /** - * @expectedException RuntimeException + * @expectedException \RuntimeException */ public function testImmutableMagicAccess() { $vars = array( @@ -96,12 +123,18 @@ class RequestTest extends \Test\TestCase { 'method' => 'GET' ); - $request = new Request($vars, $this->secureRandom, $this->stream); + $request = new Request( + $vars, + $this->secureRandom, + $this->config, + $this->stream + ); + $request->{'nickname'} = 'Janey'; } /** - * @expectedException LogicException + * @expectedException \LogicException */ public function testGetTheMethodRight() { $vars = array( @@ -109,8 +142,14 @@ class RequestTest extends \Test\TestCase { 'method' => 'GET', ); - $request = new Request($vars, $this->secureRandom, $this->stream); - $result = $request->post; + $request = new Request( + $vars, + $this->secureRandom, + $this->config, + $this->stream + ); + + $request->post; } public function testTheMethodIsRight() { @@ -119,7 +158,13 @@ class RequestTest extends \Test\TestCase { 'method' => 'GET', ); - $request = new Request($vars, $this->secureRandom, $this->stream); + $request = new Request( + $vars, + $this->secureRandom, + $this->config, + $this->stream + ); + $this->assertEquals('GET', $request->method); $result = $request->get; $this->assertEquals('John Q. Public', $result['name']); @@ -134,7 +179,13 @@ class RequestTest extends \Test\TestCase { 'server' => array('CONTENT_TYPE' => 'application/json; utf-8') ); - $request = new Request($vars, $this->secureRandom, $this->stream); + $request = new Request( + $vars, + $this->secureRandom, + $this->config, + $this->stream + ); + $this->assertEquals('POST', $request->method); $result = $request->post; $this->assertEquals('John Q. Public', $result['name']); @@ -152,7 +203,12 @@ class RequestTest extends \Test\TestCase { 'server' => array('CONTENT_TYPE' => 'application/x-www-form-urlencoded'), ); - $request = new Request($vars, $this->secureRandom, $this->stream); + $request = new Request( + $vars, + $this->secureRandom, + $this->config, + $this->stream + ); $this->assertEquals('PATCH', $request->method); $result = $request->patch; @@ -171,7 +227,12 @@ class RequestTest extends \Test\TestCase { 'server' => array('CONTENT_TYPE' => 'application/json; utf-8'), ); - $request = new Request($vars, $this->secureRandom, $this->stream); + $request = new Request( + $vars, + $this->secureRandom, + $this->config, + $this->stream + ); $this->assertEquals('PUT', $request->method); $result = $request->put; @@ -186,7 +247,12 @@ class RequestTest extends \Test\TestCase { 'server' => array('CONTENT_TYPE' => 'application/json; utf-8'), ); - $request = new Request($vars, $this->secureRandom, $this->stream); + $request = new Request( + $vars, + $this->secureRandom, + $this->config, + $this->stream + ); $this->assertEquals('PATCH', $request->method); $result = $request->patch; @@ -205,7 +271,13 @@ class RequestTest extends \Test\TestCase { 'server' => array('CONTENT_TYPE' => 'image/png'), ); - $request = new Request($vars, $this->secureRandom, $this->stream); + $request = new Request( + $vars, + $this->secureRandom, + $this->config, + $this->stream + ); + $this->assertEquals('PUT', $request->method); $resource = $request->put; $contents = stream_get_contents($resource); @@ -228,7 +300,12 @@ class RequestTest extends \Test\TestCase { 'urlParams' => array('id' => '2'), ); - $request = new Request($vars, $this->secureRandom, $this->stream); + $request = new Request( + $vars, + $this->secureRandom, + $this->config, + $this->stream + ); $newParams = array('id' => '3', 'test' => 'test2'); $request->setUrlParameters($newParams); @@ -244,7 +321,13 @@ class RequestTest extends \Test\TestCase { ], ]; - $request = new Request($vars, $this->secureRandom, $this->stream); + $request = new Request( + $vars, + $this->secureRandom, + $this->config, + $this->stream + ); + $this->assertSame('GeneratedUniqueIdByModUnique', $request->getId()); } @@ -261,14 +344,695 @@ class RequestTest extends \Test\TestCase { ->method('getLowStrengthGenerator') ->will($this->returnValue($lowRandomSource)); - $request = new Request([], $this->secureRandom, $this->stream); + $request = new Request( + [], + $this->secureRandom, + $this->config, + $this->stream + ); + $this->assertSame('GeneratedByOwnCloudItself', $request->getId()); } public function testGetIdWithoutModUniqueStable() { - $request = new Request([], \OC::$server->getSecureRandom(), $this->stream); + $request = new Request( + [], + \OC::$server->getSecureRandom(), + $this->config, + $this->stream + ); $firstId = $request->getId(); $secondId = $request->getId(); $this->assertSame($firstId, $secondId); } + + public function testGetRemoteAddressWithoutTrustedRemote() { + $this->config + ->expects($this->once()) + ->method('getSystemValue') + ->with('trusted_proxies') + ->will($this->returnValue([])); + + $request = new Request( + [ + 'server' => [ + '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' + ], + ], + $this->secureRandom, + $this->config, + $this->stream + ); + + $this->assertSame('10.0.0.2', $request->getRemoteAddress()); + } + + public function testGetRemoteAddressWithNoTrustedHeader() { + $this->config + ->expects($this->at(0)) + ->method('getSystemValue') + ->with('trusted_proxies') + ->will($this->returnValue(['10.0.0.2'])); + $this->config + ->expects($this->at(1)) + ->method('getSystemValue') + ->with('forwarded_for_headers') + ->will($this->returnValue([])); + + $request = new Request( + [ + 'server' => [ + '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' + ], + ], + $this->secureRandom, + $this->config, + $this->stream + ); + + $this->assertSame('10.0.0.2', $request->getRemoteAddress()); + } + + public function testGetRemoteAddressWithSingleTrustedRemote() { + $this->config + ->expects($this->at(0)) + ->method('getSystemValue') + ->with('trusted_proxies') + ->will($this->returnValue(['10.0.0.2'])); + $this->config + ->expects($this->at(1)) + ->method('getSystemValue') + ->with('forwarded_for_headers') + ->will($this->returnValue(['HTTP_X_FORWARDED'])); + + $request = new Request( + [ + 'server' => [ + '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' + ], + ], + $this->secureRandom, + $this->config, + $this->stream + ); + + $this->assertSame('10.4.0.5', $request->getRemoteAddress()); + } + + public function testGetRemoteAddressVerifyPriorityHeader() { + $this->config + ->expects($this->at(0)) + ->method('getSystemValue') + ->with('trusted_proxies') + ->will($this->returnValue(['10.0.0.2'])); + $this->config + ->expects($this->at(1)) + ->method('getSystemValue') + ->with('forwarded_for_headers') + ->will($this->returnValue([ + 'HTTP_CLIENT_IP', + 'HTTP_X_FORWARDED_FOR', + 'HTTP_X_FORWARDED' + ])); + + $request = new Request( + [ + 'server' => [ + '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' + ], + ], + $this->secureRandom, + $this->config, + $this->stream + ); + + $this->assertSame('192.168.0.233', $request->getRemoteAddress()); + } + + public function testGetServerProtocolWithOverride() { + $this->config + ->expects($this->at(0)) + ->method('getSystemValue') + ->with('overwriteprotocol') + ->will($this->returnValue('customProtocol')); + $this->config + ->expects($this->at(1)) + ->method('getSystemValue') + ->with('overwritecondaddr') + ->will($this->returnValue('')); + $this->config + ->expects($this->at(2)) + ->method('getSystemValue') + ->with('overwriteprotocol') + ->will($this->returnValue('customProtocol')); + + $request = new Request( + [], + $this->secureRandom, + $this->config, + $this->stream + ); + + $this->assertSame('customProtocol', $request->getServerProtocol()); + } + + public function testGetServerProtocolWithProtoValid() { + $this->config + ->expects($this->exactly(2)) + ->method('getSystemValue') + ->with('overwriteprotocol') + ->will($this->returnValue('')); + + $requestHttps = new Request( + [ + 'server' => [ + 'HTTP_X_FORWARDED_PROTO' => 'HtTpS' + ], + ], + $this->secureRandom, + $this->config, + $this->stream + ); + $requestHttp = new Request( + [ + 'server' => [ + 'HTTP_X_FORWARDED_PROTO' => 'HTTp' + ], + ], + $this->secureRandom, + $this->config, + $this->stream + ); + + + $this->assertSame('https', $requestHttps->getServerProtocol()); + $this->assertSame('http', $requestHttp->getServerProtocol()); + } + + public function testGetServerProtocolWithHttpsServerValueOn() { + $this->config + ->expects($this->once()) + ->method('getSystemValue') + ->with('overwriteprotocol') + ->will($this->returnValue('')); + + $request = new Request( + [ + 'server' => [ + 'HTTPS' => 'on' + ], + ], + $this->secureRandom, + $this->config, + $this->stream + ); + $this->assertSame('https', $request->getServerProtocol()); + } + + public function testGetServerProtocolWithHttpsServerValueOff() { + $this->config + ->expects($this->once()) + ->method('getSystemValue') + ->with('overwriteprotocol') + ->will($this->returnValue('')); + + $request = new Request( + [ + 'server' => [ + 'HTTPS' => 'off' + ], + ], + $this->secureRandom, + $this->config, + $this->stream + ); + $this->assertSame('http', $request->getServerProtocol()); + } + + public function testGetServerProtocolDefault() { + $this->config + ->expects($this->once()) + ->method('getSystemValue') + ->with('overwriteprotocol') + ->will($this->returnValue('')); + + $request = new Request( + [], + $this->secureRandom, + $this->config, + $this->stream + ); + $this->assertSame('http', $request->getServerProtocol()); + } + + /** + * @dataProvider userAgentProvider + * @param string $testAgent + * @param array $userAgent + * @param bool $matches + */ + public function testUserAgent($testAgent, $userAgent, $matches) { + $request = new Request( + [ + 'server' => [ + 'HTTP_USER_AGENT' => $testAgent, + ] + ], + $this->secureRandom, + $this->config, + $this->stream + ); + + $this->assertEquals($matches, $request->isUserAgent($userAgent)); + } + + /** + * @return array + */ + function userAgentProvider() { + return [ + [ + 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)', + [ + Request::USER_AGENT_IE + ], + true, + ], + [ + 'Mozilla/5.0 (X11; Linux i686; rv:24.0) Gecko/20100101 Firefox/24.0', + [ + Request::USER_AGENT_IE + ], + false, + ], + [ + 'Mozilla/5.0 (Linux; Android 4.4; Nexus 4 Build/KRT16S) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.59 Mobile Safari/537.36', + [ + Request::USER_AGENT_ANDROID_MOBILE_CHROME + ], + true, + ], + [ + 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)', + [ + Request::USER_AGENT_ANDROID_MOBILE_CHROME + ], + false, + ], + [ + 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)', + [ + Request::USER_AGENT_IE, + Request::USER_AGENT_ANDROID_MOBILE_CHROME, + ], + true, + ], + [ + 'Mozilla/5.0 (Linux; Android 4.4; Nexus 4 Build/KRT16S) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.59 Mobile Safari/537.36', + [ + Request::USER_AGENT_IE, + Request::USER_AGENT_ANDROID_MOBILE_CHROME, + ], + true, + ], + [ + 'Mozilla/5.0 (X11; Linux i686; rv:24.0) Gecko/20100101 Firefox/24.0', + [ + Request::USER_AGENT_FREEBOX + ], + false, + ], + [ + 'Mozilla/5.0', + [ + Request::USER_AGENT_FREEBOX + ], + true, + ], + [ + 'Fake Mozilla/5.0', + [ + Request::USER_AGENT_FREEBOX + ], + false, + ], + ]; + } + + public function testInsecureServerHostServerNameHeader() { + $request = new Request( + [ + 'server' => [ + 'SERVER_NAME' => 'from.server.name:8080', + ] + ], + $this->secureRandom, + $this->config, + $this->stream + ); + + $this->assertEquals('from.server.name:8080', $request->getInsecureServerHost()); + } + + public function testInsecureServerHostHttpHostHeader() { + $request = new Request( + [ + 'server' => [ + 'SERVER_NAME' => 'from.server.name:8080', + 'HTTP_HOST' => 'from.host.header:8080', + ] + ], + $this->secureRandom, + $this->config, + $this->stream + ); + + $this->assertEquals('from.host.header:8080', $request->getInsecureServerHost()); + } + + public function testInsecureServerHostHttpFromForwardedHeaderSingle() { + $request = new Request( + [ + 'server' => [ + 'SERVER_NAME' => 'from.server.name:8080', + 'HTTP_HOST' => 'from.host.header:8080', + 'HTTP_X_FORWARDED_HOST' => 'from.forwarded.host:8080', + ] + ], + $this->secureRandom, + $this->config, + $this->stream + ); + + $this->assertEquals('from.forwarded.host:8080', $request->getInsecureServerHost()); + } + + public function testInsecureServerHostHttpFromForwardedHeaderStacked() { + $request = new Request( + [ + 'server' => [ + 'SERVER_NAME' => 'from.server.name:8080', + 'HTTP_HOST' => 'from.host.header:8080', + 'HTTP_X_FORWARDED_HOST' => 'from.forwarded.host2:8080,another.one:9000', + ] + ], + $this->secureRandom, + $this->config, + $this->stream + ); + + $this->assertEquals('from.forwarded.host2:8080', $request->getInsecureServerHost()); + } + + public function testGetServerHost() { + $request = new Request( + [], + $this->secureRandom, + $this->config, + $this->stream + ); + + $this->assertEquals('localhost', $request->getServerHost()); + } + + public function testGetOverwriteHostDefaultNull() { + $this->config + ->expects($this->once()) + ->method('getSystemValue') + ->with('overwritehost') + ->will($this->returnValue('')); + $request = new Request( + [], + $this->secureRandom, + $this->config, + $this->stream + ); + + $this->assertNull(\Test_Helper::invokePrivate($request, 'getOverwriteHost')); + } + + public function testGetOverwriteHostWithOverwrite() { + $this->config + ->expects($this->at(0)) + ->method('getSystemValue') + ->with('overwritehost') + ->will($this->returnValue('www.owncloud.org')); + $this->config + ->expects($this->at(1)) + ->method('getSystemValue') + ->with('overwritecondaddr') + ->will($this->returnValue('')); + $this->config + ->expects($this->at(2)) + ->method('getSystemValue') + ->with('overwritehost') + ->will($this->returnValue('www.owncloud.org')); + + $request = new Request( + [], + $this->secureRandom, + $this->config, + $this->stream + ); + + $this->assertSame('www.owncloud.org', \Test_Helper::invokePrivate($request, 'getOverwriteHost')); + } + + public function testGetPathInfoWithSetEnv() { + $request = new Request( + [ + 'server' => [ + 'PATH_INFO' => 'apps/files/', + ] + ], + $this->secureRandom, + $this->config, + $this->stream + ); + + $this->assertEquals('apps/files/', $request->getPathInfo()); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage The requested uri(/foo.php) cannot be processed by the script '/var/www/index.php') + */ + public function testGetPathInfoNotProcessible() { + $request = new Request( + [ + 'server' => [ + 'REQUEST_URI' => '/foo.php', + 'SCRIPT_NAME' => '/var/www/index.php', + ] + ], + $this->secureRandom, + $this->config, + $this->stream + ); + + $request->getPathInfo(); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage The requested uri(/foo.php) cannot be processed by the script '/var/www/index.php') + */ + public function testGetRawPathInfoNotProcessible() { + $request = new Request( + [ + 'server' => [ + 'REQUEST_URI' => '/foo.php', + 'SCRIPT_NAME' => '/var/www/index.php', + ] + ], + $this->secureRandom, + $this->config, + $this->stream + ); + + $request->getRawPathInfo(); + } + + /** + * @dataProvider genericPathInfoProvider + * @param string $requestUri + * @param string $scriptName + * @param string $expected + */ + public function testGetPathInfoWithoutSetEnvGeneric($requestUri, $scriptName, $expected) { + $request = new Request( + [ + 'server' => [ + 'REQUEST_URI' => $requestUri, + 'SCRIPT_NAME' => $scriptName, + ] + ], + $this->secureRandom, + $this->config, + $this->stream + ); + + $this->assertEquals($expected, $request->getPathInfo()); + } + + /** + * @dataProvider genericPathInfoProvider + * @param string $requestUri + * @param string $scriptName + * @param string $expected + */ + public function testGetRawPathInfoWithoutSetEnvGeneric($requestUri, $scriptName, $expected) { + $request = new Request( + [ + 'server' => [ + 'REQUEST_URI' => $requestUri, + 'SCRIPT_NAME' => $scriptName, + ] + ], + $this->secureRandom, + $this->config, + $this->stream + ); + + $this->assertEquals($expected, $request->getRawPathInfo()); + } + + /** + * @dataProvider rawPathInfoProvider + * @param string $requestUri + * @param string $scriptName + * @param string $expected + */ + public function testGetRawPathInfoWithoutSetEnv($requestUri, $scriptName, $expected) { + $request = new Request( + [ + 'server' => [ + 'REQUEST_URI' => $requestUri, + 'SCRIPT_NAME' => $scriptName, + ] + ], + $this->secureRandom, + $this->config, + $this->stream + ); + + $this->assertEquals($expected, $request->getRawPathInfo()); + } + + /** + * @dataProvider pathInfoProvider + * @param string $requestUri + * @param string $scriptName + * @param string $expected + */ + public function testGetPathInfoWithoutSetEnv($requestUri, $scriptName, $expected) { + $request = new Request( + [ + 'server' => [ + 'REQUEST_URI' => $requestUri, + 'SCRIPT_NAME' => $scriptName, + ] + ], + $this->secureRandom, + $this->config, + $this->stream + ); + + $this->assertEquals($expected, $request->getPathInfo()); + } + + /** + * @return array + */ + public function genericPathInfoProvider() { + return [ + ['/index.php/apps/files/', 'index.php', '/apps/files/'], + ['/index.php/apps/files/../&/&?someQueryParameter=QueryParam', 'index.php', '/apps/files/../&/&'], + ['/remote.php/漢字編碼方法 / 汉字编码方法', 'remote.php', '/漢字編碼方法 / 汉字编码方法'], + ['///removeTrailin//gSlashes///', 'remote.php', '/removeTrailin/gSlashes/'], + ['/', '/', ''], + ['', '', ''], + ]; + } + + /** + * @return array + */ + public function rawPathInfoProvider() { + return [ + ['/foo%2Fbar/subfolder', '', 'foo%2Fbar/subfolder'], + ]; + } + + /** + * @return array + */ + public function pathInfoProvider() { + return [ + ['/foo%2Fbar/subfolder', '', 'foo/bar/subfolder'], + ]; + } + + public function testGetRequestUriWithoutOverwrite() { + $this->config + ->expects($this->once()) + ->method('getSystemValue') + ->with('overwritewebroot') + ->will($this->returnValue('')); + + $request = new Request( + [ + 'server' => [ + 'REQUEST_URI' => '/test.php' + ] + ], + $this->secureRandom, + $this->config, + $this->stream + ); + + $this->assertSame('/test.php', $request->getRequestUri()); + } + + public function testGetRequestUriWithOverwrite() { + $this->config + ->expects($this->at(0)) + ->method('getSystemValue') + ->with('overwritewebroot') + ->will($this->returnValue('/owncloud/')); + $this->config + ->expects($this->at(1)) + ->method('getSystemValue') + ->with('overwritecondaddr') + ->will($this->returnValue('')); + + $request = $this->getMockBuilder('\OC\AppFramework\Http\Request') + ->setMethods(['getScriptName']) + ->setConstructorArgs([ + [ + 'server' => [ + 'REQUEST_URI' => '/test.php/some/PathInfo', + 'SCRIPT_NAME' => '/test.php', + ] + ], + $this->secureRandom, + $this->config, + $this->stream + ]) + ->getMock(); + $request + ->expects($this->once()) + ->method('getScriptName') + ->will($this->returnValue('/scriptname.php')); + + $this->assertSame('/scriptname.php/some/PathInfo', $request->getRequestUri()); + } } -- cgit v1.2.3