From 886bda5f81d52ba4443094e4c2fffac33c27bc4b Mon Sep 17 00:00:00 2001 From: Lukas Reschke Date: Tue, 10 Feb 2015 13:02:48 +0100 Subject: [PATCH] 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 --- lib/base.php | 40 +- lib/private/app.php | 5 +- lib/private/appframework/http/request.php | 288 ++++++- lib/private/connector/sabre/file.php | 30 +- lib/private/connector/sabre/request.php | 2 +- lib/private/installer.php | 1 - lib/private/log/owncloud.php | 5 +- lib/private/request.php | 330 ------- lib/private/response.php | 11 +- lib/private/route/router.php | 5 +- lib/private/security/trusteddomainhelper.php | 75 ++ lib/private/server.php | 86 +- lib/private/setup.php | 6 +- lib/private/template.php | 5 +- lib/private/templatelayout.php | 10 +- lib/private/urlgenerator.php | 3 +- lib/private/util.php | 7 +- lib/public/irequest.php | 65 ++ lib/public/util.php | 12 +- ocs/v1.php | 2 +- public.php | 9 +- remote.php | 13 +- settings/admin.php | 5 +- .../controller/ApiControllerTest.php | 3 +- .../controller/ControllerTest.php | 3 +- .../controller/OCSControllerTest.php | 19 +- .../dependencyinjection/DIContainerTest.php | 3 +- .../lib/appframework/http/DispatcherTest.php | 30 +- tests/lib/appframework/http/RequestTest.php | 804 +++++++++++++++++- .../middleware/MiddlewareDispatcherTest.php | 3 +- .../middleware/MiddlewareTest.php | 10 +- .../security/CORSMiddlewareTest.php | 17 +- .../security/SecurityMiddlewareTest.php | 3 +- .../middleware/sessionmiddlewaretest.php | 3 +- tests/lib/request.php | 333 -------- tests/lib/security/trusteddomainhelper.php | 70 ++ tests/lib/templatelayout.php | 2 +- 37 files changed, 1496 insertions(+), 822 deletions(-) delete mode 100644 lib/private/request.php create mode 100644 lib/private/security/trusteddomainhelper.php delete mode 100644 tests/lib/request.php create mode 100644 tests/lib/security/trusteddomainhelper.php diff --git a/lib/base.php b/lib/base.php index db75895857..51d59d130a 100644 --- a/lib/base.php +++ b/lib/base.php @@ -100,7 +100,11 @@ class OC { OC_Config::$object = new \OC\Config(self::$configDir); OC::$SUBURI = str_replace("\\", "/", substr(realpath($_SERVER["SCRIPT_FILENAME"]), strlen(OC::$SERVERROOT))); - $scriptName = OC_Request::scriptName(); + /** + * FIXME: The following line is required because of a cyclic dependency + * on IRequest. + */ + $scriptName = $_SERVER['SCRIPT_NAME']; if (substr($scriptName, -1) == '/') { $scriptName .= 'index.php'; //make sure suburi follows the same rules as scriptName @@ -230,6 +234,8 @@ class OC { } public static function checkSSL() { + $request = \OC::$server->getRequest(); + // redirect to https site if configured if (\OC::$server->getSystemConfig()->getValue('forcessl', false)) { // Default HSTS policy @@ -241,14 +247,15 @@ class OC { } header($header); ini_set('session.cookie_secure', 'on'); - if (OC_Request::serverProtocol() <> 'https' and !OC::$CLI) { - $url = 'https://' . OC_Request::serverHost() . OC_Request::requestUri(); + + if ($request->getServerProtocol() <> 'https' && !OC::$CLI) { + $url = 'https://' . $request->getServerHost() . $request->getRequestUri(); header("Location: $url"); exit(); } } else { // Invalidate HSTS headers - if (OC_Request::serverProtocol() === 'https') { + if ($request->getServerProtocol() === 'https') { header('Strict-Transport-Security: max-age=0'); } } @@ -612,18 +619,24 @@ class OC { return; } - $host = OC_Request::insecureServerHost(); - // if the host passed in headers isn't trusted + $trustedDomainHelper = new \OC\Security\TrustedDomainHelper(\OC::$server->getConfig()); + $request = \OC::$server->getRequest(); + $host = $request->getInsecureServerHost(); + /** + * if the host passed in headers isn't trusted + * FIXME: Should not be in here at all :see_no_evil: + */ if (!OC::$CLI - // overwritehost is always trusted - && OC_Request::getOverwriteHost() === null - && !OC_Request::isTrustedDomain($host) + // overwritehost is always trusted, workaround to not have to make + // \OC\AppFramework\Http\Request::getOverwriteHost public + && self::$server->getConfig()->getSystemValue('overwritehost') === '' + && !$trustedDomainHelper->isTrustedDomain($host) ) { header('HTTP/1.1 400 Bad Request'); header('Status: 400 Bad Request'); $tmpl = new OCP\Template('core', 'untrustedDomain', 'guest'); - $tmpl->assign('domain', $_SERVER['SERVER_NAME']); + $tmpl->assign('domain', $request->server['SERVER_NAME']); $tmpl->printPage(); exit(); @@ -720,6 +733,7 @@ class OC { * Handle the request */ public static function handleRequest() { + \OC::$server->getEventLogger()->start('handle_request', 'Handle request'); $systemConfig = \OC::$server->getSystemConfig(); // load all the classpaths from the enabled apps so they are available @@ -734,7 +748,7 @@ class OC { exit(); } - $request = OC_Request::getPathInfo(); + $request = \OC::$server->getRequest()->getPathInfo(); if (substr($request, -3) !== '.js') { // we need these files during the upgrade self::checkMaintenanceMode(); self::checkUpgrade(); @@ -764,7 +778,7 @@ class OC { } self::checkSingleUserMode(); OC_Util::setupFS(); - OC::$server->getRouter()->match(OC_Request::getRawPathInfo()); + OC::$server->getRouter()->match(\OC::$server->getRequest()->getRawPathInfo()); return; } catch (Symfony\Component\Routing\Exception\ResourceNotFoundException $e) { //header('HTTP/1.0 404 Not Found'); @@ -895,7 +909,7 @@ class OC { // if return is true we are logged in -> redirect to the default page if ($return === true) { - $_REQUEST['redirect_url'] = \OC_Request::requestUri(); + $_REQUEST['redirect_url'] = \OC::$server->getRequest()->getRequestUri(); OC_Util::redirectToDefaultPage(); exit; } diff --git a/lib/private/app.php b/lib/private/app.php index f41cb82c36..1af2c36e19 100644 --- a/lib/private/app.php +++ b/lib/private/app.php @@ -665,10 +665,11 @@ class OC_App { * @return string */ public static function getCurrentApp() { - $script = substr(OC_Request::scriptName(), strlen(OC::$WEBROOT) + 1); + $request = \OC::$server->getRequest(); + $script = substr($request->getScriptName(), strlen(OC::$WEBROOT) + 1); $topFolder = substr($script, 0, strpos($script, '/')); if (empty($topFolder)) { - $path_info = OC_Request::getPathInfo(); + $path_info = $request->getPathInfo(); if ($path_info) { $topFolder = substr($path_info, 1, strpos($path_info, '/', 1) - 1); } diff --git a/lib/private/appframework/http/request.php b/lib/private/appframework/http/request.php index 4902671d4b..f11d189962 100644 --- a/lib/private/appframework/http/request.php +++ b/lib/private/appframework/http/request.php @@ -24,6 +24,8 @@ namespace OC\AppFramework\Http; +use OC\Security\TrustedDomainHelper; +use OCP\IConfig; use OCP\IRequest; use OCP\Security\ISecureRandom; @@ -31,9 +33,14 @@ use OCP\Security\ISecureRandom; * Class for accessing variables in the request. * This class provides an immutable object with request variables. */ - class Request implements \ArrayAccess, \Countable, IRequest { + const USER_AGENT_IE = '/MSIE/'; + // Android Chrome user agent: https://developers.google.com/chrome/mobile/docs/user-agent + const USER_AGENT_ANDROID_MOBILE_CHROME = '#Android.*Chrome/[.0-9]*#'; + const USER_AGENT_FREEBOX = '#^Mozilla/5\.0$#'; + const REGEX_LOCALHOST = '/^(127\.0\.0\.1|localhost)$/'; + protected $inputStream; protected $content; protected $items = array(); @@ -51,6 +58,8 @@ class Request implements \ArrayAccess, \Countable, IRequest { ); /** @var ISecureRandom */ protected $secureRandom; + /** @var IConfig */ + protected $config; /** @var string */ protected $requestId = ''; @@ -66,15 +75,18 @@ class Request implements \ArrayAccess, \Countable, IRequest { * - string 'method' the request method (GET, POST etc) * - string|false 'requesttoken' the requesttoken or false when not available * @param ISecureRandom $secureRandom + * @param IConfig $config * @param string $stream * @see http://www.php.net/manual/en/reserved.variables.php */ public function __construct(array $vars=array(), ISecureRandom $secureRandom, + IConfig $config, $stream='php://input') { $this->inputStream = $stream; $this->items['params'] = array(); $this->secureRandom = $secureRandom; + $this->config = $config; if(!array_key_exists('method', $vars)) { $vars['method'] = 'GET'; @@ -115,7 +127,9 @@ class Request implements \ArrayAccess, \Countable, IRequest { ); } - + /** + * @param $parameters + */ public function setUrlParameters($parameters) { $this->items['urlParams'] = $parameters; $this->items['parameters'] = array_merge( @@ -124,7 +138,10 @@ class Request implements \ArrayAccess, \Countable, IRequest { ); } - // Countable method. + /** + * Countable method + * @return int + */ public function count() { return count(array_keys($this->items['parameters'])); } @@ -176,7 +193,11 @@ class Request implements \ArrayAccess, \Countable, IRequest { throw new \RuntimeException('You cannot change the contents of the request object'); } - // Magic property accessors + /** + * Magic property accessors + * @param $name + * @param $value + */ public function __set($name, $value) { throw new \RuntimeException('You cannot change the contents of the request object'); } @@ -231,12 +252,17 @@ class Request implements \ArrayAccess, \Countable, IRequest { } } - + /** + * @param $name + * @return bool + */ public function __isset($name) { return isset($this->items['parameters'][$name]); } - + /** + * @param $id + */ public function __unset($id) { throw new \RunTimeException('You cannot change the contents of the request object'); } @@ -412,4 +438,254 @@ class Request implements \ArrayAccess, \Countable, IRequest { return $this->requestId; } + /** + * Returns the remote address, if the connection came from a trusted proxy + * and `forwarded_for_headers` has been configured then the IP address + * specified in this header will be returned instead. + * Do always use this instead of $_SERVER['REMOTE_ADDR'] + * @return string IP address + */ + public function getRemoteAddress() { + $remoteAddress = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : ''; + $trustedProxies = $this->config->getSystemValue('trusted_proxies', []); + + if(is_array($trustedProxies) && in_array($remoteAddress, $trustedProxies)) { + $forwardedForHeaders = $this->config->getSystemValue('forwarded_for_headers', []); + + foreach($forwardedForHeaders as $header) { + if(isset($this->server[$header])) { + foreach(explode(',', $this->server[$header]) as $IP) { + $IP = trim($IP); + if (filter_var($IP, FILTER_VALIDATE_IP) !== false) { + return $IP; + } + } + } + } + } + + return $remoteAddress; + } + + /** + * Check overwrite condition + * @param string $type + * @return bool + */ + private function isOverwriteCondition($type = '') { + $regex = '/' . $this->config->getSystemValue('overwritecondaddr', '') . '/'; + return $regex === '//' || preg_match($regex, $this->server['REMOTE_ADDR']) === 1 + || ($type !== 'protocol' && $this->config->getSystemValue('forcessl', false)); + } + + /** + * Returns the server protocol. It respects reverse proxy servers and load + * balancers. + * @return string Server protocol (http or https) + */ + public function getServerProtocol() { + if($this->config->getSystemValue('overwriteprotocol') !== '' + && $this->isOverwriteCondition('protocol')) { + return $this->config->getSystemValue('overwriteprotocol'); + } + + if (isset($this->server['HTTP_X_FORWARDED_PROTO'])) { + $proto = strtolower($this->server['HTTP_X_FORWARDED_PROTO']); + // Verify that the protocol is always HTTP or HTTPS + // default to http if an invalid value is provided + return $proto === 'https' ? 'https' : 'http'; + } + + if (isset($this->server['HTTPS']) + && $this->server['HTTPS'] !== null + && $this->server['HTTPS'] !== 'off') { + return 'https'; + } + + return 'http'; + } + + /** + * Returns the request uri, even if the website uses one or more + * reverse proxies + * @return string + */ + public function getRequestUri() { + $uri = isset($this->server['REQUEST_URI']) ? $this->server['REQUEST_URI'] : ''; + if($this->config->getSystemValue('overwritewebroot') !== '' && $this->isOverwriteCondition()) { + $uri = $this->getScriptName() . substr($uri, strlen($this->server['SCRIPT_NAME'])); + } + return $uri; + } + + /** + * Get raw PathInfo from request (not urldecoded) + * @throws \Exception + * @return string|false Path info or false when not found + */ + public function getRawPathInfo() { + $requestUri = isset($this->server['REQUEST_URI']) ? $this->server['REQUEST_URI'] : ''; + // remove too many leading slashes - can be caused by reverse proxy configuration + if (strpos($requestUri, '/') === 0) { + $requestUri = '/' . ltrim($requestUri, '/'); + } + + $requestUri = preg_replace('%/{2,}%', '/', $requestUri); + + // Remove the query string from REQUEST_URI + if ($pos = strpos($requestUri, '?')) { + $requestUri = substr($requestUri, 0, $pos); + } + + $scriptName = $this->server['SCRIPT_NAME']; + $pathInfo = $requestUri; + + // strip off the script name's dir and file name + // FIXME: Sabre does not really belong here + list($path, $name) = \Sabre\DAV\URLUtil::splitPath($scriptName); + if (!empty($path)) { + if($path === $pathInfo || strpos($pathInfo, $path.'/') === 0) { + $pathInfo = substr($pathInfo, strlen($path)); + } else { + throw new \Exception("The requested uri($requestUri) cannot be processed by the script '$scriptName')"); + } + } + if (strpos($pathInfo, '/'.$name) === 0) { + $pathInfo = substr($pathInfo, strlen($name) + 1); + } + if (strpos($pathInfo, $name) === 0) { + $pathInfo = substr($pathInfo, strlen($name)); + } + if($pathInfo === '/'){ + return ''; + } else { + return $pathInfo; + } + } + + /** + * Get PathInfo from request + * @throws \Exception + * @return string|false Path info or false when not found + */ + public function getPathInfo() { + if(isset($this->server['PATH_INFO'])) { + return $this->server['PATH_INFO']; + } + + $pathInfo = $this->getRawPathInfo(); + // following is taken from \Sabre\DAV\URLUtil::decodePathSegment + $pathInfo = rawurldecode($pathInfo); + $encoding = mb_detect_encoding($pathInfo, ['UTF-8', 'ISO-8859-1']); + + switch($encoding) { + case 'ISO-8859-1' : + $pathInfo = utf8_encode($pathInfo); + } + // end copy + + return $pathInfo; + } + + /** + * Returns the script name, even if the website uses one or more + * reverse proxies + * @return string the script name + */ + public function getScriptName() { + $name = $this->server['SCRIPT_NAME']; + $overwriteWebRoot = $this->config->getSystemValue('overwritewebroot'); + if ($overwriteWebRoot !== '' && $this->isOverwriteCondition()) { + // FIXME: This code is untestable due to ___DIR__ + $serverRoot = str_replace('\\', '/', substr(__DIR__, 0, -strlen('lib/private/'))); + $suburi = str_replace('\\', '/', substr(realpath($this->server['SCRIPT_FILENAME']), strlen($serverRoot))); + $name = '/' . ltrim($overwriteWebRoot . $suburi, '/'); + } + return $name; + } + + /** + * Checks whether the user agent matches a given regex + * @param array $agent array of agent names + * @return bool true if at least one of the given agent matches, false otherwise + */ + public function isUserAgent(array $agent) { + foreach ($agent as $regex) { + if (preg_match($regex, $this->server['HTTP_USER_AGENT'])) { + return true; + } + } + return false; + } + + /** + * Returns the unverified server host from the headers without checking + * whether it is a trusted domain + * @return string Server host + */ + public function getInsecureServerHost() { + $host = null; + if (isset($this->server['HTTP_X_FORWARDED_HOST'])) { + if (strpos($this->server['HTTP_X_FORWARDED_HOST'], ',') !== false) { + $parts = explode(',', $this->server['HTTP_X_FORWARDED_HOST']); + $host = trim(current($parts)); + } else { + $host = $this->server['HTTP_X_FORWARDED_HOST']; + } + } else { + if (isset($this->server['HTTP_HOST'])) { + $host = $this->server['HTTP_HOST']; + } else if (isset($this->server['SERVER_NAME'])) { + $host = $this->server['SERVER_NAME']; + } + } + return $host; + } + + + /** + * Returns the server host from the headers, or the first configured + * trusted domain if the host isn't in the trusted list + * @return string Server host + */ + public function getServerHost() { + // FIXME: Ugly workaround that we need to get rid of + if (\OC::$CLI && defined('PHPUNIT_RUN')) { + return 'localhost'; + } + + // overwritehost is always trusted + $host = $this->getOverwriteHost(); + if ($host !== null) { + return $host; + } + + // get the host from the headers + $host = $this->getInsecureServerHost(); + + // Verify that the host is a trusted domain if the trusted domains + // are defined + // If no trusted domain is provided the first trusted domain is returned + $trustedDomainHelper = new TrustedDomainHelper($this->config); + if ($trustedDomainHelper->isTrustedDomain($host)) { + return $host; + } else { + $trustedList = $this->config->getSystemValue('trusted_domains', []); + return $trustedList[0]; + } + } + + /** + * Returns the overwritehost setting from the config if set and + * if the overwrite condition is met + * @return string|null overwritehost value or null if not defined or the defined condition + * isn't met + */ + private function getOverwriteHost() { + if($this->config->getSystemValue('overwritehost') !== '' && $this->isOverwriteCondition()) { + return $this->config->getSystemValue('overwritehost'); + } + return null; + } + } diff --git a/lib/private/connector/sabre/file.php b/lib/private/connector/sabre/file.php index e57d04f9a6..bb672696f2 100644 --- a/lib/private/connector/sabre/file.php +++ b/lib/private/connector/sabre/file.php @@ -149,9 +149,9 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements \Sabre\ } // allow sync clients to send the mtime along in a header - $mtime = OC_Request::hasModificationTime(); - if ($mtime !== false) { - if($this->fileView->touch($this->path, $mtime)) { + $request = \OC::$server->getRequest(); + if (isset($request->server['HTTP_X_OC_MTIME'])) { + if($this->fileView->touch($this->path, $request->server['HTTP_X_OC_MTIME'])) { header('X-OC-MTime: accepted'); } } @@ -165,8 +165,9 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements \Sabre\ /** * Returns the data - * * @return string|resource + * @throws \Sabre\DAV\Exception\Forbidden + * @throws \Sabre\DAV\Exception\ServiceUnavailable */ public function get() { @@ -187,9 +188,8 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements \Sabre\ /** * Delete the current file - * - * @return void * @throws \Sabre\DAV\Exception\Forbidden + * @throws \Sabre\DAV\Exception\ServiceUnavailable */ public function delete() { if (!$this->info->isDeletable()) { @@ -251,6 +251,9 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements \Sabre\ return \OC_Helper::getSecureMimeType($mimeType); } + /** + * @return array|false + */ public function getDirectDownload() { if (\OCP\App::isEnabled('encryption')) { return []; @@ -267,6 +270,10 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements \Sabre\ /** * @param resource $data * @return null|string + * @throws \Sabre\DAV\Exception + * @throws \Sabre\DAV\Exception\BadRequest + * @throws \Sabre\DAV\Exception\NotImplemented + * @throws \Sabre\DAV\Exception\ServiceUnavailable */ private function createFileChunked($data) { @@ -319,9 +326,9 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements \Sabre\ } // allow sync clients to send the mtime along in a header - $mtime = OC_Request::hasModificationTime(); - if ($mtime !== false) { - if($this->fileView->touch($targetPath, $mtime)) { + $request = \OC::$server->getRequest(); + if (isset($request->server['HTTP_X_OC_MTIME'])) { + if($this->fileView->touch($targetPath, $request->server['HTTP_X_OC_MTIME'])) { header('X-OC-MTime: accepted'); } } @@ -340,9 +347,8 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements \Sabre\ * Returns whether a part file is needed for the given storage * or whether the file can be assembled/uploaded directly on the * target storage. - * - * @param \OCP\Files\Storage $storage storage to check - * @param bool true if the storage needs part file handling + * @param \OCP\Files\Storage $storage + * @return bool true if the storage needs part file handling */ private function needsPartFile($storage) { // TODO: in the future use ChunkHandler provided by storage diff --git a/lib/private/connector/sabre/request.php b/lib/private/connector/sabre/request.php index c98b28c4d7..2ce753d916 100644 --- a/lib/private/connector/sabre/request.php +++ b/lib/private/connector/sabre/request.php @@ -28,7 +28,7 @@ class OC_Connector_Sabre_Request extends \Sabre\HTTP\Request { * @return string */ public function getUri() { - return OC_Request::requestUri(); + return \OC::$server->getRequest()->getRequestUri(); } /** diff --git a/lib/private/installer.php b/lib/private/installer.php index aeac3497fd..8ed15a3a5d 100644 --- a/lib/private/installer.php +++ b/lib/private/installer.php @@ -529,7 +529,6 @@ class OC_Installer{ * @param string $folder the folder of the app to check * @return boolean true for app is o.k. and false for app is not o.k. */ - public static function checkCode($folder) { // is the code checker enabled? if(!OC_Config::getValue('appcodechecker', false)) { diff --git a/lib/private/log/owncloud.php b/lib/private/log/owncloud.php index 8e55bf1c69..62c2da6feb 100644 --- a/lib/private/log/owncloud.php +++ b/lib/private/log/owncloud.php @@ -68,8 +68,9 @@ class OC_Log_Owncloud { $timezone = new DateTimeZone('UTC'); } $time = new DateTime(null, $timezone); - $reqId = \OC::$server->getRequest()->getId(); - $remoteAddr = \OC_Request::getRemoteAddress(); + $request = \OC::$server->getRequest(); + $reqId = $request->getId(); + $remoteAddr = $request->getRemoteAddress(); // remove username/passwords from URLs before writing the to the log file $time = $time->format($format); if($minLevel == OC_Log::DEBUG) { diff --git a/lib/private/request.php b/lib/private/request.php deleted file mode 100644 index ab011c913d..0000000000 --- a/lib/private/request.php +++ /dev/null @@ -1,330 +0,0 @@ - - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. - */ - -class OC_Request { - - const USER_AGENT_IE = '/MSIE/'; - // Android Chrome user agent: https://developers.google.com/chrome/mobile/docs/user-agent - const USER_AGENT_ANDROID_MOBILE_CHROME = '#Android.*Chrome/[.0-9]*#'; - const USER_AGENT_FREEBOX = '#^Mozilla/5\.0$#'; - const REGEX_LOCALHOST = '/^(127\.0\.0\.1|localhost)$/'; - - /** - * Returns the remote address, if the connection came from a trusted proxy and `forwarded_for_headers` has been configured - * then the IP address specified in this header will be returned instead. - * Do always use this instead of $_SERVER['REMOTE_ADDR'] - * @return string IP address - */ - public static function getRemoteAddress() { - $remoteAddress = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : ''; - $trustedProxies = \OC::$server->getConfig()->getSystemValue('trusted_proxies', array()); - - if(is_array($trustedProxies) && in_array($remoteAddress, $trustedProxies)) { - $forwardedForHeaders = \OC::$server->getConfig()->getSystemValue('forwarded_for_headers', array()); - - foreach($forwardedForHeaders as $header) { - if (array_key_exists($header, $_SERVER) === true) { - foreach (explode(',', $_SERVER[$header]) as $IP) { - $IP = trim($IP); - if (filter_var($IP, FILTER_VALIDATE_IP) !== false) { - return $IP; - } - } - } - } - } - - return $remoteAddress; - } - - /** - * Check overwrite condition - * @param string $type - * @return bool - */ - private static function isOverwriteCondition($type = '') { - $regex = '/' . OC_Config::getValue('overwritecondaddr', '') . '/'; - return $regex === '//' or preg_match($regex, $_SERVER['REMOTE_ADDR']) === 1 - or ($type !== 'protocol' and OC_Config::getValue('forcessl', false)); - } - - /** - * Strips a potential port from a domain (in format domain:port) - * @param $host - * @return string $host without appended port - */ - public static function getDomainWithoutPort($host) { - $pos = strrpos($host, ':'); - if ($pos !== false) { - $port = substr($host, $pos + 1); - if (is_numeric($port)) { - $host = substr($host, 0, $pos); - } - } - return $host; - } - - /** - * Checks whether a domain is considered as trusted from the list - * of trusted domains. If no trusted domains have been configured, returns - * true. - * This is used to prevent Host Header Poisoning. - * @param string $domainWithPort - * @return bool true if the given domain is trusted or if no trusted domains - * have been configured - */ - public static function isTrustedDomain($domainWithPort) { - // Extract port from domain if needed - $domain = self::getDomainWithoutPort($domainWithPort); - - // FIXME: Empty config array defaults to true for now. - Deprecate this behaviour with ownCloud 8. - $trustedList = \OC::$server->getConfig()->getSystemValue('trusted_domains', array()); - if (empty($trustedList)) { - return true; - } - - // FIXME: Workaround for older instances still with port applied. Remove for ownCloud 9. - if(in_array($domainWithPort, $trustedList)) { - return true; - } - - // Always allow access from localhost - if (preg_match(self::REGEX_LOCALHOST, $domain) === 1) { - return true; - } - - return in_array($domain, $trustedList); - } - - /** - * Returns the unverified server host from the headers without checking - * whether it is a trusted domain - * @return string the server host - * - * Returns the server host, even if the website uses one or more - * reverse proxies - */ - public static function insecureServerHost() { - $host = null; - if (isset($_SERVER['HTTP_X_FORWARDED_HOST'])) { - if (strpos($_SERVER['HTTP_X_FORWARDED_HOST'], ",") !== false) { - $parts = explode(',', $_SERVER['HTTP_X_FORWARDED_HOST']); - $host = trim(current($parts)); - } else { - $host = $_SERVER['HTTP_X_FORWARDED_HOST']; - } - } else { - if (isset($_SERVER['HTTP_HOST'])) { - $host = $_SERVER['HTTP_HOST']; - } else if (isset($_SERVER['SERVER_NAME'])) { - $host = $_SERVER['SERVER_NAME']; - } - } - return $host; - } - - /** - * Returns the overwritehost setting from the config if set and - * if the overwrite condition is met - * @return string|null overwritehost value or null if not defined or the defined condition - * isn't met - */ - public static function getOverwriteHost() { - if(OC_Config::getValue('overwritehost', '') !== '' and self::isOverwriteCondition()) { - return OC_Config::getValue('overwritehost'); - } - return null; - } - - /** - * Returns the server host from the headers, or the first configured - * trusted domain if the host isn't in the trusted list - * @return string the server host - * - * Returns the server host, even if the website uses one or more - * reverse proxies - */ - public static function serverHost() { - if (OC::$CLI && defined('PHPUNIT_RUN')) { - return 'localhost'; - } - - // overwritehost is always trusted - $host = self::getOverwriteHost(); - if ($host !== null) { - return $host; - } - - // get the host from the headers - $host = self::insecureServerHost(); - - // Verify that the host is a trusted domain if the trusted domains - // are defined - // If no trusted domain is provided the first trusted domain is returned - if (self::isTrustedDomain($host)) { - return $host; - } else { - $trustedList = \OC_Config::getValue('trusted_domains', array('')); - return $trustedList[0]; - } - } - - /** - * Returns the server protocol - * @return string the server protocol - * - * Returns the server protocol. It respects reverse proxy servers and load balancers - */ - public static function serverProtocol() { - if(OC_Config::getValue('overwriteprotocol', '') !== '' and self::isOverwriteCondition('protocol')) { - return OC_Config::getValue('overwriteprotocol'); - } - if (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) { - $proto = strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']); - // Verify that the protocol is always HTTP or HTTPS - // default to http if an invalid value is provided - return $proto === 'https' ? 'https' : 'http'; - } - if (isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') { - return 'https'; - } - return 'http'; - } - - /** - * Returns the request uri - * @return string the request uri - * - * Returns the request uri, even if the website uses one or more - * reverse proxies - * @return string - */ - public static function requestUri() { - $uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : ''; - if (OC_Config::getValue('overwritewebroot', '') !== '' and self::isOverwriteCondition()) { - $uri = self::scriptName() . substr($uri, strlen($_SERVER['SCRIPT_NAME'])); - } - return $uri; - } - - /** - * Returns the script name - * @return string the script name - * - * Returns the script name, even if the website uses one or more - * reverse proxies - */ - public static function scriptName() { - $name = $_SERVER['SCRIPT_NAME']; - $overwriteWebRoot = OC_Config::getValue('overwritewebroot', ''); - if ($overwriteWebRoot !== '' and self::isOverwriteCondition()) { - $serverroot = str_replace("\\", '/', substr(__DIR__, 0, -strlen('lib/private/'))); - $suburi = str_replace("\\", "/", substr(realpath($_SERVER["SCRIPT_FILENAME"]), strlen($serverroot))); - $name = '/' . ltrim($overwriteWebRoot . $suburi, '/'); - } - return $name; - } - - /** - * get Path info from request - * @return string Path info or false when not found - */ - public static function getPathInfo() { - if (array_key_exists('PATH_INFO', $_SERVER)) { - $path_info = $_SERVER['PATH_INFO']; - }else{ - $path_info = self::getRawPathInfo(); - // following is taken from \Sabre\DAV\URLUtil::decodePathSegment - $path_info = rawurldecode($path_info); - $encoding = mb_detect_encoding($path_info, array('UTF-8', 'ISO-8859-1')); - - switch($encoding) { - - case 'ISO-8859-1' : - $path_info = utf8_encode($path_info); - - } - // end copy - } - return $path_info; - } - - /** - * get Path info from request, not urldecoded - * @throws Exception - * @return string Path info or false when not found - */ - public static function getRawPathInfo() { - $requestUri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : ''; - // remove too many leading slashes - can be caused by reverse proxy configuration - if (strpos($requestUri, '/') === 0) { - $requestUri = '/' . ltrim($requestUri, '/'); - } - - $requestUri = preg_replace('%/{2,}%', '/', $requestUri); - - // Remove the query string from REQUEST_URI - if ($pos = strpos($requestUri, '?')) { - $requestUri = substr($requestUri, 0, $pos); - } - - $scriptName = $_SERVER['SCRIPT_NAME']; - $path_info = $requestUri; - - // strip off the script name's dir and file name - list($path, $name) = \Sabre\DAV\URLUtil::splitPath($scriptName); - if (!empty($path)) { - if( $path === $path_info || strpos($path_info, $path.'/') === 0) { - $path_info = substr($path_info, strlen($path)); - } else { - throw new Exception("The requested uri($requestUri) cannot be processed by the script '$scriptName')"); - } - } - if (strpos($path_info, '/'.$name) === 0) { - $path_info = substr($path_info, strlen($name) + 1); - } - if (strpos($path_info, $name) === 0) { - $path_info = substr($path_info, strlen($name)); - } - if($path_info === '/'){ - return ''; - } else { - return $path_info; - } - } - - /** - * Check if the requester sent along an mtime - * @return false or an mtime - */ - static public function hasModificationTime () { - if (isset($_SERVER['HTTP_X_OC_MTIME'])) { - return $_SERVER['HTTP_X_OC_MTIME']; - } else { - return false; - } - } - - /** - * Checks whether the user agent matches a given regex - * @param string|array $agent agent name or array of agent names - * @return boolean true if at least one of the given agent matches, - * false otherwise - */ - static public function isUserAgent($agent) { - if (!is_array($agent)) { - $agent = array($agent); - } - foreach ($agent as $regex) { - if (preg_match($regex, $_SERVER['HTTP_USER_AGENT'])) { - return true; - } - } - return false; - } -} diff --git a/lib/private/response.php b/lib/private/response.php index cf18115111..9be5d75c31 100644 --- a/lib/private/response.php +++ b/lib/private/response.php @@ -158,11 +158,12 @@ class OC_Response { * @param string $type disposition type, either 'attachment' or 'inline' */ static public function setContentDispositionHeader( $filename, $type = 'attachment' ) { - if (OC_Request::isUserAgent(array( - OC_Request::USER_AGENT_IE, - OC_Request::USER_AGENT_ANDROID_MOBILE_CHROME, - OC_Request::USER_AGENT_FREEBOX - ))) { + if (\OC::$server->getRequest()->isUserAgent( + [ + \OC\AppFramework\Http\Request::USER_AGENT_IE, + \OC\AppFramework\Http\Request::USER_AGENT_ANDROID_MOBILE_CHROME, + \OC\AppFramework\Http\Request::USER_AGENT_FREEBOX, + ])) { header( 'Content-Disposition: ' . rawurlencode($type) . '; filename="' . rawurlencode( $filename ) . '"' ); } else { header( 'Content-Disposition: ' . rawurlencode($type) . '; filename*=UTF-8\'\'' . rawurlencode( $filename ) diff --git a/lib/private/route/router.php b/lib/private/route/router.php index 3559b84192..25e897123d 100644 --- a/lib/private/route/router.php +++ b/lib/private/route/router.php @@ -63,8 +63,9 @@ class Router implements IRouter { } else { $method = 'GET'; } - $host = \OC_Request::serverHost(); - $schema = \OC_Request::serverProtocol(); + $request = \OC::$server->getRequest(); + $host = $request->getServerHost(); + $schema = $request->getServerProtocol(); $this->context = new RequestContext($baseUrl, $method, $host, $schema); // TODO cache $this->root = $this->getCollection('root'); diff --git a/lib/private/security/trusteddomainhelper.php b/lib/private/security/trusteddomainhelper.php new file mode 100644 index 0000000000..593263897b --- /dev/null +++ b/lib/private/security/trusteddomainhelper.php @@ -0,0 +1,75 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Security; +use OC\AppFramework\Http\Request; +use OCP\IConfig; + +/** + * Class TrustedDomain + * + * @package OC\Security + */ +class TrustedDomainHelper { + /** @var IConfig */ + private $config; + + /** + * @param IConfig $config + */ + function __construct(IConfig $config) { + $this->config = $config; + } + + /** + * Strips a potential port from a domain (in format domain:port) + * @param $host + * @return string $host without appended port + */ + private function getDomainWithoutPort($host) { + $pos = strrpos($host, ':'); + if ($pos !== false) { + $port = substr($host, $pos + 1); + if (is_numeric($port)) { + $host = substr($host, 0, $pos); + } + } + return $host; + } + + /** + * Checks whether a domain is considered as trusted from the list + * of trusted domains. If no trusted domains have been configured, returns + * true. + * This is used to prevent Host Header Poisoning. + * @param string $domainWithPort + * @return bool true if the given domain is trusted or if no trusted domains + * have been configured + */ + public function isTrustedDomain($domainWithPort) { + $domain = $this->getDomainWithoutPort($domainWithPort); + + // Read trusted domains from config + $trustedList = $this->config->getSystemValue('trusted_domains', []); + if(!is_array($trustedList)) { + return false; + } + + // TODO: Workaround for older instances still with port applied. Remove for ownCloud 9. + if(in_array($domainWithPort, $trustedList)) { + return true; + } + + // Always allow access from localhost + if (preg_match(Request::REGEX_LOCALHOST, $domain) === 1) { + return true; + } + return in_array($domain, $trustedList); + } + +} diff --git a/lib/private/server.php b/lib/private/server.php index 9660597b39..9422f332eb 100644 --- a/lib/private/server.php +++ b/lib/private/server.php @@ -41,45 +41,6 @@ class Server extends SimpleContainer implements IServerContainer { $this->registerService('ContactsManager', function ($c) { return new ContactsManager(); }); - $this->registerService('Request', function (Server $c) { - if (isset($c['urlParams'])) { - $urlParams = $c['urlParams']; - } else { - $urlParams = array(); - } - - if ($c->getSession()->exists('requesttoken')) { - $requestToken = $c->getSession()->get('requesttoken'); - } else { - $requestToken = false; - } - - if (defined('PHPUNIT_RUN') && PHPUNIT_RUN - && in_array('fakeinput', stream_get_wrappers()) - ) { - $stream = 'fakeinput://data'; - } else { - $stream = 'php://input'; - } - - return new Request( - [ - 'get' => $_GET, - 'post' => $_POST, - 'files' => $_FILES, - 'server' => $_SERVER, - 'env' => $_ENV, - 'cookies' => $_COOKIE, - 'method' => (isset($_SERVER) && isset($_SERVER['REQUEST_METHOD'])) - ? $_SERVER['REQUEST_METHOD'] - : null, - 'urlParams' => $urlParams, - 'requesttoken' => $requestToken, - ], - $this->getSecureRandom(), - $stream - ); - }); $this->registerService('PreviewManager', function ($c) { return new PreviewManager(); }); @@ -313,12 +274,57 @@ class Server extends SimpleContainer implements IServerContainer { * currently being processed is returned from this method. * In case the current execution was not initiated by a web request null is returned * + * FIXME: This should be queried as well. However, due to our totally awesome + * static code a lot of tests do stuff like $_SERVER['foo'] which obviously + * will not work with that approach. We even have some integration tests in our + * unit tests which setup a complete webserver. Once the code is all non-static + * or we don't have such mixed integration/unit tests setup anymore this can + * get moved out again. + * * @return \OCP\IRequest|null */ function getRequest() { - return $this->query('Request'); + if (isset($this['urlParams'])) { + $urlParams = $this['urlParams']; + } else { + $urlParams = array(); + } + + if ($this->getSession()->exists('requesttoken')) { + $requestToken = $this->getSession()->get('requesttoken'); + } else { + $requestToken = false; + } + + if (defined('PHPUNIT_RUN') && PHPUNIT_RUN + && in_array('fakeinput', stream_get_wrappers()) + ) { + $stream = 'fakeinput://data'; + } else { + $stream = 'php://input'; + } + + return new Request( + [ + 'get' => $_GET, + 'post' => $_POST, + 'files' => $_FILES, + 'server' => $_SERVER, + 'env' => $_ENV, + 'cookies' => $_COOKIE, + 'method' => (isset($_SERVER) && isset($_SERVER['REQUEST_METHOD'])) + ? $_SERVER['REQUEST_METHOD'] + : null, + 'urlParams' => $urlParams, + 'requesttoken' => $requestToken, + ], + $this->getSecureRandom(), + $this->getConfig(), + $stream + ); } + /** * Returns the preview manager which can create preview images for a given file * diff --git a/lib/private/setup.php b/lib/private/setup.php index e3a29b6469..a3b46c1eb4 100644 --- a/lib/private/setup.php +++ b/lib/private/setup.php @@ -157,12 +157,14 @@ class OC_Setup { return $error; } + $request = \OC::$server->getRequest(); + //no errors, good if(isset($options['trusted_domains']) && is_array($options['trusted_domains'])) { $trustedDomains = $options['trusted_domains']; } else { - $trustedDomains = array(\OC_Request::getDomainWithoutPort(\OC_Request::serverHost())); + $trustedDomains = [\OCP\Util::getServerHostName()]; } if (OC_Util::runningOnWindows()) { @@ -185,7 +187,7 @@ class OC_Setup { 'secret' => $secret, 'trusted_domains' => $trustedDomains, 'datadirectory' => $dataDir, - 'overwrite.cli.url' => \OC_Request::serverProtocol() . '://' . \OC_Request::serverHost() . OC::$WEBROOT, + 'overwrite.cli.url' => $request->getServerProtocol() . '://' . $request->getServerHost() . OC::$WEBROOT, 'dbtype' => $dbType, 'version' => implode('.', OC_Util::getVersion()), ]); diff --git a/lib/private/template.php b/lib/private/template.php index 4fa1c867d5..b0d212c6f5 100644 --- a/lib/private/template.php +++ b/lib/private/template.php @@ -215,6 +215,7 @@ class OC_Template extends \OC\Template\Base { * @param Exception $exception */ public static function printExceptionErrorPage(Exception $exception) { + $request = \OC::$server->getRequest(); $content = new \OC_Template('', 'exception', 'error', false); $content->assign('errorMsg', $exception->getMessage()); $content->assign('errorCode', $exception->getCode()); @@ -222,8 +223,8 @@ class OC_Template extends \OC\Template\Base { $content->assign('line', $exception->getLine()); $content->assign('trace', $exception->getTraceAsString()); $content->assign('debugMode', defined('DEBUG') && DEBUG === true); - $content->assign('remoteAddr', OC_Request::getRemoteAddress()); - $content->assign('requestID', \OC::$server->getRequest()->getId()); + $content->assign('remoteAddr', $request->getRemoteAddress()); + $content->assign('requestID', $request->getId()); $content->printPage(); die(); } diff --git a/lib/private/templatelayout.php b/lib/private/templatelayout.php index 1a97eb2634..1678795f52 100644 --- a/lib/private/templatelayout.php +++ b/lib/private/templatelayout.php @@ -34,9 +34,9 @@ class OC_TemplateLayout extends OC_Template { $this->config = \OC::$server->getConfig(); // Decide which page we show - if( $renderAs == 'user' ) { + if($renderAs == 'user') { parent::__construct( 'core', 'layout.user' ); - if(in_array(OC_APP::getCurrentApp(), array('settings','admin', 'help'))!==false) { + if(in_array(OC_App::getCurrentApp(), ['settings','admin', 'help']) !== false) { $this->assign('bodyid', 'body-settings'); }else{ $this->assign('bodyid', 'body-user'); @@ -72,9 +72,9 @@ class OC_TemplateLayout extends OC_Template { } } $userDisplayName = OC_User::getDisplayName(); - $this->assign( 'user_displayname', $userDisplayName ); - $this->assign( 'user_uid', OC_User::getUser() ); - $this->assign( 'appsmanagement_active', strpos(OC_Request::requestUri(), OC_Helper::linkToRoute('settings_apps')) === 0 ); + $this->assign('user_displayname', $userDisplayName); + $this->assign('user_uid', OC_User::getUser()); + $this->assign('appsmanagement_active', strpos(\OC::$server->getRequest()->getRequestUri(), OC_Helper::linkToRoute('settings_apps')) === 0 ); $this->assign('enableAvatars', $this->config->getSystemValue('enable_avatars', true)); $this->assign('userAvatarSet', \OC_Helper::userAvatarSet(OC_User::getUser())); } else if ($renderAs == 'error') { diff --git a/lib/private/urlgenerator.php b/lib/private/urlgenerator.php index 0bf8ce2299..e87a6c354f 100644 --- a/lib/private/urlgenerator.php +++ b/lib/private/urlgenerator.php @@ -170,7 +170,8 @@ class URLGenerator implements IURLGenerator { ? '' : \OC::$WEBROOT; - return \OC_Request::serverProtocol() . '://' . \OC_Request::serverHost(). $webRoot . $separator . $url; + $request = \OC::$server->getRequest(); + return $request->getServerProtocol() . '://' . $request->getServerHost() . $webRoot . $separator . $url; } /** diff --git a/lib/private/util.php b/lib/private/util.php index 2be7e8eb29..1993a7c9a9 100644 --- a/lib/private/util.php +++ b/lib/private/util.php @@ -849,8 +849,11 @@ class OC_Util { // Check if we are a user if (!OC_User::isLoggedIn()) { header('Location: ' . OC_Helper::linkToAbsolute('', 'index.php', - array('redirect_url' => OC_Request::requestUri()) - )); + [ + 'redirect_url' => \OC::$server->getRequest()->getRequestUri() + ] + ) + ); exit(); } } diff --git a/lib/public/irequest.php b/lib/public/irequest.php index b5ea1fa95d..814fc0251c 100644 --- a/lib/public/irequest.php +++ b/lib/public/irequest.php @@ -134,4 +134,69 @@ interface IRequest { * @return string */ public function getId(); + + /** + * Returns the remote address, if the connection came from a trusted proxy + * and `forwarded_for_headers` has been configured then the IP address + * specified in this header will be returned instead. + * Do always use this instead of $_SERVER['REMOTE_ADDR'] + * @return string IP address + */ + public function getRemoteAddress(); + + /** + * Returns the server protocol. It respects reverse proxy servers and load + * balancers. + * @return string Server protocol (http or https) + */ + public function getServerProtocol(); + + /** + * Returns the request uri, even if the website uses one or more + * reverse proxies + * @return string + */ + public function getRequestUri(); + + /** + * Get raw PathInfo from request (not urldecoded) + * @throws \Exception + * @return string|false Path info or false when not found + */ + public function getRawPathInfo(); + + /** + * Get PathInfo from request + * @throws \Exception + * @return string|false Path info or false when not found + */ + public function getPathInfo(); + + /** + * Returns the script name, even if the website uses one or more + * reverse proxies + * @return string the script name + */ + public function getScriptName(); + + /** + * Checks whether the user agent matches a given regex + * @param array $agent array of agent names + * @return bool true if at least one of the given agent matches, false otherwise + */ + public function isUserAgent(array $agent); + + /** + * Returns the unverified server host from the headers without checking + * whether it is a trusted domain + * @return string Server host + */ + public function getInsecureServerHost(); + + /** + * Returns the server host from the headers, or the first configured + * trusted domain if the host isn't in the trusted list + * @return string Server host + */ + public function getServerHost(); } diff --git a/lib/public/util.php b/lib/public/util.php index 7f1974a421..e6e14a26e0 100644 --- a/lib/public/util.php +++ b/lib/public/util.php @@ -234,9 +234,10 @@ class Util { /** * Returns the server host, even if the website uses one or more reverse proxy * @return string the server host + * @deprecated Use \OCP\IRequest::getServerHost */ public static function getServerHost() { - return(\OC_Request::serverHost()); + return \OC::$server->getRequest()->getServerHost(); } /** @@ -285,25 +286,28 @@ class Util { /** * Returns the server protocol. It respects reverse proxy servers and load balancers * @return string the server protocol + * @deprecated Use \OCP\IRequest::getServerProtocol */ public static function getServerProtocol() { - return(\OC_Request::serverProtocol()); + return \OC::$server->getRequest()->getServerProtocol(); } /** * Returns the request uri, even if the website uses one or more reverse proxies * @return string the request uri + * @deprecated Use \OCP\IRequest::getRequestUri */ public static function getRequestUri() { - return(\OC_Request::requestUri()); + return \OC::$server->getRequest()->getRequestUri(); } /** * Returns the script name, even if the website uses one or more reverse proxies * @return string the script name + * @deprecated Use \OCP\IRequest::getScriptName */ public static function getScriptName() { - return(\OC_Request::scriptName()); + return \OC::$server->getRequest()->getScriptName(); } /** diff --git a/ocs/v1.php b/ocs/v1.php index 0a86fb0641..b0f3e5e2b9 100644 --- a/ocs/v1.php +++ b/ocs/v1.php @@ -42,7 +42,7 @@ try { // api calls always will return English \OC_L10N::forceLanguage('en'); - OC::$server->getRouter()->match('/ocs'.OC_Request::getRawPathInfo()); + OC::$server->getRouter()->match('/ocs'.\OC::$server->getRequest()->getRawPathInfo()); } catch (ResourceNotFoundException $e) { OC_API::setContentType(); OC_OCS::notFound(); diff --git a/public.php b/public.php index c5c227ef46..f17052a6c8 100644 --- a/public.php +++ b/public.php @@ -13,12 +13,13 @@ try { OC::checkMaintenanceMode(); OC::checkSingleUserMode(); - $pathInfo = OC_Request::getPathInfo(); - if (!$pathInfo && !isset($_GET['service'])) { + $request = \OC::$server->getRequest(); + $pathInfo = $request->getPathInfo(); + if (!$pathInfo && !isset($request->server['service'])) { header('HTTP/1.0 404 Not Found'); exit; - } elseif (isset($_GET['service'])) { - $service = $_GET['service']; + } elseif (isset($request->server['service'])) { + $service = $request->server['service']; } else { $pathInfo = trim($pathInfo, '/'); list($service) = explode('/', $pathInfo); diff --git a/remote.php b/remote.php index 7993566afe..80173441e9 100644 --- a/remote.php +++ b/remote.php @@ -11,17 +11,18 @@ try { exit; } - $path_info = OC_Request::getPathInfo(); - if ($path_info === false || $path_info === '') { + $request = \OC::$server->getRequest(); + $pathInfo = $request->getPathInfo(); + if ($pathInfo === false || $pathInfo === '') { OC_Response::setStatus(OC_Response::STATUS_NOT_FOUND); exit; } - if (!$pos = strpos($path_info, '/', 1)) { - $pos = strlen($path_info); + if (!$pos = strpos($pathInfo, '/', 1)) { + $pos = strlen($pathInfo); } - $service=substr($path_info, 1, $pos-1); + $service=substr($pathInfo, 1, $pos-1); - $file = \OC::$server->getAppConfig()->getValue('core', 'remote_' . $service); + $file = \OC::$server->getConfig()->getAppValue('core', 'remote_' . $service); if(is_null($file)) { OC_Response::setStatus(OC_Response::STATUS_NOT_FOUND); diff --git a/settings/admin.php b/settings/admin.php index bb20c665f5..cdbc2700a8 100644 --- a/settings/admin.php +++ b/settings/admin.php @@ -20,6 +20,7 @@ $doesLogFileExist = file_exists($logFilePath); $logFileSize = filesize($logFilePath); $config = \OC::$server->getConfig(); $appConfig = \OC::$server->getAppConfig(); +$request = \OC::$server->getRequest(); // Should we display sendmail as an option? $template->assign('sendmail_is_available', (bool) \OC_Helper::findBinaryPath('sendmail')); @@ -59,7 +60,7 @@ $excludedGroupsList = explode(',', $excludedGroupsList); // FIXME: this should b $template->assign('shareExcludedGroupsList', implode('|', $excludedGroupsList)); // Check if connected using HTTPS -$template->assign('isConnectedViaHTTPS', OC_Request::serverProtocol() === 'https'); +$template->assign('isConnectedViaHTTPS', $request->getServerProtocol() === 'https'); $template->assign('enforceHTTPSEnabled', $config->getSystemValue('forcessl', false)); $template->assign('forceSSLforSubdomainsEnabled', $config->getSystemValue('forceSSLforSubdomains', false)); @@ -88,7 +89,7 @@ $template->assign('WindowsWarning', OC_Util::runningOnWindows()); $forms = OC_App::getForms('admin'); $l = OC_L10N::get('settings'); $formsAndMore = array(); -if (OC_Request::serverProtocol() !== 'https' || !OC_Util::isAnnotationsWorking() || +if ($request->getServerProtocol() !== 'https' || !OC_Util::isAnnotationsWorking() || $suggestedOverwriteCliUrl || !OC_Util::isSetLocaleWorking() || !OC_Util::isPhpCharSetUtf8() || !OC_Util::fileInfoLoaded() || $databaseOverload ) { diff --git a/tests/lib/appframework/controller/ApiControllerTest.php b/tests/lib/appframework/controller/ApiControllerTest.php index b2e52cc0b5..137e5950f6 100644 --- a/tests/lib/appframework/controller/ApiControllerTest.php +++ b/tests/lib/appframework/controller/ApiControllerTest.php @@ -37,7 +37,8 @@ class ApiControllerTest extends \Test\TestCase { public function testCors() { $request = new Request( ['server' => ['HTTP_ORIGIN' => 'test']], - $this->getMockBuilder('\OCP\Security\ISecureRandom')->getMock() + $this->getMock('\OCP\Security\ISecureRandom'), + $this->getMock('\OCP\IConfig') ); $this->controller = new ChildApiController('app', $request, 'verbs', 'headers', 100); diff --git a/tests/lib/appframework/controller/ControllerTest.php b/tests/lib/appframework/controller/ControllerTest.php index 58395d0591..78c0d9d15a 100644 --- a/tests/lib/appframework/controller/ControllerTest.php +++ b/tests/lib/appframework/controller/ControllerTest.php @@ -75,7 +75,8 @@ class ControllerTest extends \Test\TestCase { 'session' => ['sezession' => 'kein'], 'method' => 'hi', ], - $this->getMockBuilder('\OCP\Security\ISecureRandom')->getMock() + $this->getMock('\OCP\Security\ISecureRandom'), + $this->getMock('\OCP\IConfig') ); $this->app = $this->getMock('OC\AppFramework\DependencyInjection\DIContainer', diff --git a/tests/lib/appframework/controller/OCSControllerTest.php b/tests/lib/appframework/controller/OCSControllerTest.php index 3b4de1d7a0..11a9d45eb9 100644 --- a/tests/lib/appframework/controller/OCSControllerTest.php +++ b/tests/lib/appframework/controller/OCSControllerTest.php @@ -33,11 +33,17 @@ class ChildOCSController extends OCSController {} class OCSControllerTest extends \Test\TestCase { + private $controller; public function testCors() { $request = new Request( - array('server' => array('HTTP_ORIGIN' => 'test')), - $this->getMock('\OCP\Security\ISecureRandom') + [ + 'server' => [ + 'HTTP_ORIGIN' => 'test', + ], + ], + $this->getMock('\OCP\Security\ISecureRandom'), + $this->getMock('\OCP\IConfig') ); $controller = new ChildOCSController('app', $request, 'verbs', 'headers', 100); @@ -57,7 +63,8 @@ class OCSControllerTest extends \Test\TestCase { public function testXML() { $controller = new ChildOCSController('app', new Request( [], - $this->getMock('\OCP\Security\ISecureRandom') + $this->getMock('\OCP\Security\ISecureRandom'), + $this->getMock('\OCP\IConfig') )); $expected = "\n" . "\n" . @@ -86,7 +93,8 @@ class OCSControllerTest extends \Test\TestCase { public function testXMLDataResponse() { $controller = new ChildOCSController('app', new Request( [], - $this->getMock('\OCP\Security\ISecureRandom') + $this->getMock('\OCP\Security\ISecureRandom'), + $this->getMock('\OCP\IConfig') )); $expected = "\n" . "\n" . @@ -115,7 +123,8 @@ class OCSControllerTest extends \Test\TestCase { public function testJSON() { $controller = new ChildOCSController('app', new Request( [], - $this->getMock('\OCP\Security\ISecureRandom') + $this->getMock('\OCP\Security\ISecureRandom'), + $this->getMock('\OCP\IConfig') )); $expected = '{"status":"OK","statuscode":400,"message":"OK",' . '"totalitems":"","itemsperpage":"","data":{"test":"hi"}}'; diff --git a/tests/lib/appframework/dependencyinjection/DIContainerTest.php b/tests/lib/appframework/dependencyinjection/DIContainerTest.php index 43309f64e6..0cbdddbb20 100644 --- a/tests/lib/appframework/dependencyinjection/DIContainerTest.php +++ b/tests/lib/appframework/dependencyinjection/DIContainerTest.php @@ -73,7 +73,8 @@ class DIContainerTest extends \Test\TestCase { public function testMiddlewareDispatcherIncludesSecurityMiddleware(){ $this->container['Request'] = new Request( ['method' => 'GET'], - $this->getMockBuilder('\OCP\Security\ISecureRandom')->getMock() + $this->getMock('\OCP\Security\ISecureRandom'), + $this->getMock('\OCP\IConfig') ); $security = $this->container['SecurityMiddleware']; $dispatcher = $this->container['MiddlewareDispatcher']; diff --git a/tests/lib/appframework/http/DispatcherTest.php b/tests/lib/appframework/http/DispatcherTest.php index 832cd80e60..02c86df8e7 100644 --- a/tests/lib/appframework/http/DispatcherTest.php +++ b/tests/lib/appframework/http/DispatcherTest.php @@ -24,7 +24,6 @@ namespace OC\AppFramework\Http; -use OC\AppFramework\Middleware\MiddlewareDispatcher; use OC\AppFramework\Utility\ControllerMethodReflector; use OCP\AppFramework\Http; use OCP\AppFramework\Http\JSONResponse; @@ -33,6 +32,10 @@ use OCP\AppFramework\Controller; class TestController extends Controller { + /** + * @param string $appName + * @param \OCP\IRequest $request + */ public function __construct($appName, $request) { parent::__construct($appName, $request); } @@ -40,6 +43,9 @@ class TestController extends Controller { /** * @param int $int * @param bool $bool + * @param int $test + * @param int $test2 + * @return array */ public function exec($int, $bool, $test=4, $test2=1) { $this->registerResponder('text', function($in) { @@ -52,6 +58,9 @@ class TestController extends Controller { /** * @param int $int * @param bool $bool + * @param int $test + * @param int $test2 + * @return DataResponse */ public function execDataResponse($int, $bool, $test=4, $test2=1) { return new DataResponse(array( @@ -67,6 +76,7 @@ class DispatcherTest extends \Test\TestCase { private $dispatcher; private $controllerMethod; private $response; + private $request; private $lastModified; private $etag; private $http; @@ -284,7 +294,8 @@ class DispatcherTest extends \Test\TestCase { ], 'method' => 'POST' ], - $this->getMockBuilder('\OCP\Security\ISecureRandom')->getMock() + $this->getMock('\OCP\Security\ISecureRandom'), + $this->getMock('\OCP\IConfig') ); $this->dispatcher = new Dispatcher( $this->http, $this->middlewareDispatcher, $this->reflector, @@ -310,7 +321,8 @@ class DispatcherTest extends \Test\TestCase { ], 'method' => 'POST', ], - $this->getMockBuilder('\OCP\Security\ISecureRandom')->getMock() + $this->getMock('\OCP\Security\ISecureRandom'), + $this->getMock('\OCP\IConfig') ); $this->dispatcher = new Dispatcher( $this->http, $this->middlewareDispatcher, $this->reflector, @@ -339,7 +351,8 @@ class DispatcherTest extends \Test\TestCase { ], 'method' => 'GET' ], - $this->getMockBuilder('\OCP\Security\ISecureRandom')->getMock() + $this->getMock('\OCP\Security\ISecureRandom'), + $this->getMock('\OCP\IConfig') ); $this->dispatcher = new Dispatcher( $this->http, $this->middlewareDispatcher, $this->reflector, @@ -367,7 +380,8 @@ class DispatcherTest extends \Test\TestCase { ], 'method' => 'GET' ], - $this->getMockBuilder('\OCP\Security\ISecureRandom')->getMock() + $this->getMock('\OCP\Security\ISecureRandom'), + $this->getMock('\OCP\IConfig') ); $this->dispatcher = new Dispatcher( $this->http, $this->middlewareDispatcher, $this->reflector, @@ -396,7 +410,8 @@ class DispatcherTest extends \Test\TestCase { ], 'method' => 'PUT' ], - $this->getMockBuilder('\OCP\Security\ISecureRandom')->getMock() + $this->getMock('\OCP\Security\ISecureRandom'), + $this->getMock('\OCP\IConfig') ); $this->dispatcher = new Dispatcher( $this->http, $this->middlewareDispatcher, $this->reflector, @@ -427,7 +442,8 @@ class DispatcherTest extends \Test\TestCase { ], 'method' => 'POST' ], - $this->getMockBuilder('\OCP\Security\ISecureRandom')->getMock() + $this->getMock('\OCP\Security\ISecureRandom'), + $this->getMock('\OCP\IConfig') ); $this->dispatcher = new Dispatcher( $this->http, $this->middlewareDispatcher, $this->reflector, diff --git a/tests/lib/appframework/http/RequestTest.php b/tests/lib/appframework/http/RequestTest.php index eeba64b7f6..3185a0093c 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()); + } } diff --git a/tests/lib/appframework/middleware/MiddlewareDispatcherTest.php b/tests/lib/appframework/middleware/MiddlewareDispatcherTest.php index 078543c7b5..a873152579 100644 --- a/tests/lib/appframework/middleware/MiddlewareDispatcherTest.php +++ b/tests/lib/appframework/middleware/MiddlewareDispatcherTest.php @@ -132,7 +132,8 @@ class MiddlewareDispatcherTest extends \Test\TestCase { ['app', new Request( ['method' => 'GET'], - $this->getMockBuilder('\OCP\Security\ISecureRandom')->getMock() + $this->getMock('\OCP\Security\ISecureRandom'), + $this->getMock('\OCP\IConfig') ) ] ); diff --git a/tests/lib/appframework/middleware/MiddlewareTest.php b/tests/lib/appframework/middleware/MiddlewareTest.php index fcc0c300a8..33f04e1383 100644 --- a/tests/lib/appframework/middleware/MiddlewareTest.php +++ b/tests/lib/appframework/middleware/MiddlewareTest.php @@ -26,7 +26,7 @@ namespace OC\AppFramework; use OC\AppFramework\Http\Request; use OCP\AppFramework\Middleware; - +use OCP\AppFramework\Http\Response; class ChildMiddleware extends Middleware {}; @@ -40,6 +40,8 @@ class MiddlewareTest extends \Test\TestCase { private $controller; private $exception; private $api; + /** @var Response */ + private $response; protected function setUp(){ parent::setUp(); @@ -56,7 +58,11 @@ class MiddlewareTest extends \Test\TestCase { [], [ $this->api, - new Request([], $this->getMockBuilder('\OCP\Security\ISecureRandom')->getMock()) + new Request( + [], + $this->getMock('\OCP\Security\ISecureRandom'), + $this->getMock('\OCP\IConfig') + ) ] ); $this->exception = new \Exception(); diff --git a/tests/lib/appframework/middleware/security/CORSMiddlewareTest.php b/tests/lib/appframework/middleware/security/CORSMiddlewareTest.php index 57a7c524ab..a4f3137cb1 100644 --- a/tests/lib/appframework/middleware/security/CORSMiddlewareTest.php +++ b/tests/lib/appframework/middleware/security/CORSMiddlewareTest.php @@ -37,7 +37,8 @@ class CORSMiddlewareTest extends \Test\TestCase { 'HTTP_ORIGIN' => 'test' ] ], - $this->getMockBuilder('\OCP\Security\ISecureRandom')->getMock() + $this->getMock('\OCP\Security\ISecureRandom'), + $this->getMock('\OCP\IConfig') ); $this->reflector->reflect($this, __FUNCTION__); $middleware = new CORSMiddleware($request, $this->reflector); @@ -55,7 +56,8 @@ class CORSMiddlewareTest extends \Test\TestCase { 'HTTP_ORIGIN' => 'test' ] ], - $this->getMockBuilder('\OCP\Security\ISecureRandom')->getMock() + $this->getMock('\OCP\Security\ISecureRandom'), + $this->getMock('\OCP\IConfig') ); $middleware = new CORSMiddleware($request, $this->reflector); @@ -69,7 +71,11 @@ class CORSMiddlewareTest extends \Test\TestCase { * @CORS */ public function testNoOriginHeaderNoCORSHEADER() { - $request = new Request([], $this->getMockBuilder('\OCP\Security\ISecureRandom')->getMock()); + $request = new Request( + [], + $this->getMock('\OCP\Security\ISecureRandom'), + $this->getMock('\OCP\IConfig') + ); $this->reflector->reflect($this, __FUNCTION__); $middleware = new CORSMiddleware($request, $this->reflector); @@ -90,14 +96,15 @@ class CORSMiddlewareTest extends \Test\TestCase { 'HTTP_ORIGIN' => 'test' ] ], - $this->getMockBuilder('\OCP\Security\ISecureRandom')->getMock() + $this->getMock('\OCP\Security\ISecureRandom'), + $this->getMock('\OCP\IConfig') ); $this->reflector->reflect($this, __FUNCTION__); $middleware = new CORSMiddleware($request, $this->reflector); $response = new Response(); $response->addHeader('AcCess-control-Allow-Credentials ', 'TRUE'); - $response = $middleware->afterController($this, __FUNCTION__, $response); + $middleware->afterController($this, __FUNCTION__, $response); } } diff --git a/tests/lib/appframework/middleware/security/SecurityMiddlewareTest.php b/tests/lib/appframework/middleware/security/SecurityMiddlewareTest.php index 3acba7ce1d..347a0423ea 100644 --- a/tests/lib/appframework/middleware/security/SecurityMiddlewareTest.php +++ b/tests/lib/appframework/middleware/security/SecurityMiddlewareTest.php @@ -321,7 +321,8 @@ class SecurityMiddlewareTest extends \Test\TestCase { 'REQUEST_URI' => 'owncloud/index.php/apps/specialapp' ] ], - $this->getMockBuilder('\OCP\Security\ISecureRandom')->getMock() + $this->getMock('\OCP\Security\ISecureRandom'), + $this->getMock('\OCP\IConfig') ); $this->middleware = $this->getMiddleware(true, true); $response = $this->middleware->afterException($this->controller, 'test', diff --git a/tests/lib/appframework/middleware/sessionmiddlewaretest.php b/tests/lib/appframework/middleware/sessionmiddlewaretest.php index c417225d90..11c1600f51 100644 --- a/tests/lib/appframework/middleware/sessionmiddlewaretest.php +++ b/tests/lib/appframework/middleware/sessionmiddlewaretest.php @@ -35,7 +35,8 @@ class SessionMiddlewareTest extends \Test\TestCase { $this->request = new Request( [], - $this->getMockBuilder('\OCP\Security\ISecureRandom')->getMock() + $this->getMockBuilder('\OCP\Security\ISecureRandom')->getMock(), + $this->getMock('\OCP\IConfig') ); $this->reflector = new ControllerMethodReflector(); } diff --git a/tests/lib/request.php b/tests/lib/request.php deleted file mode 100644 index dd6d1e47cd..0000000000 --- a/tests/lib/request.php +++ /dev/null @@ -1,333 +0,0 @@ - - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. - */ - -class Test_Request extends \Test\TestCase { - - protected function setUp() { - parent::setUp(); - - OC::$server->getConfig()->setSystemValue('overwritewebroot', '/domain.tld/ownCloud'); - - OC::$server->getConfig()->setSystemValue('trusted_proxies', array()); - OC::$server->getConfig()->setSystemValue('forwarded_for_headers', array()); - } - - protected function tearDown() { - OC::$server->getConfig()->setSystemValue('overwritewebroot', ''); - OC::$server->getConfig()->setSystemValue('trusted_proxies', array()); - OC::$server->getConfig()->setSystemValue('forwarded_for_headers', array()); - - parent::tearDown(); - } - - public function testScriptNameOverWrite() { - $_SERVER['REMOTE_ADDR'] = '10.0.0.1'; - $_SERVER['SCRIPT_FILENAME'] = __FILE__; - - $scriptName = OC_Request::scriptName(); - $this->assertEquals('/domain.tld/ownCloud/tests/lib/request.php', $scriptName); - } - - public function testGetRemoteAddress() { - $_SERVER['REMOTE_ADDR'] = '10.0.0.2'; - $_SERVER['HTTP_X_FORWARDED'] = '10.4.0.5, 10.4.0.4'; - $_SERVER['HTTP_X_FORWARDED_FOR'] = '192.168.0.233'; - - // Without having specified a trusted remote address - $this->assertEquals('10.0.0.2', OC_Request::getRemoteAddress()); - - // With specifying a trusted remote address but no trusted header - OC::$server->getConfig()->setSystemValue('trusted_proxies', array('10.0.0.2')); - $this->assertEquals('10.0.0.2', OC_Request::getRemoteAddress()); - - // With specifying a trusted remote address and trusted headers - OC::$server->getConfig()->setSystemValue('trusted_proxies', array('10.0.0.2')); - OC::$server->getConfig()->setSystemValue('forwarded_for_headers', array('HTTP_X_FORWARDED')); - $this->assertEquals('10.4.0.5', OC_Request::getRemoteAddress()); - OC::$server->getConfig()->setSystemValue('forwarded_for_headers', array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED')); - $this->assertEquals('192.168.0.233', OC_Request::getRemoteAddress()); - - // With specifying multiple trusted remote addresses and trusted headers - OC::$server->getConfig()->setSystemValue('trusted_proxies', array('10.3.4.2', '10.0.0.2', '127.0.3.3')); - OC::$server->getConfig()->setSystemValue('forwarded_for_headers', array('HTTP_X_FORWARDED')); - $this->assertEquals('10.4.0.5', OC_Request::getRemoteAddress()); - OC::$server->getConfig()->setSystemValue('forwarded_for_headers', array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED')); - $this->assertEquals('192.168.0.233', OC_Request::getRemoteAddress()); - } - - /** - * @dataProvider rawPathInfoProvider - * @param $expected - * @param $requestUri - * @param $scriptName - */ - public function testRawPathInfo($expected, $requestUri, $scriptName) { - $_SERVER['REQUEST_URI'] = $requestUri; - $_SERVER['SCRIPT_NAME'] = $scriptName; - $rawPathInfo = OC_Request::getRawPathInfo(); - $this->assertEquals($expected, $rawPathInfo); - } - - function rawPathInfoProvider() { - return array( - array('/core/ajax/translations.php', 'index.php/core/ajax/translations.php', 'index.php'), - array('/core/ajax/translations.php', '/index.php/core/ajax/translations.php', '/index.php'), - array('/core/ajax/translations.php', '//index.php/core/ajax/translations.php', '/index.php'), - array('', '/oc/core', '/oc/core/index.php'), - array('', '/oc/core/', '/oc/core/index.php'), - array('', '/oc/core/index.php', '/oc/core/index.php'), - array('/core/ajax/translations.php', '/core/ajax/translations.php', 'index.php'), - array('/core/ajax/translations.php', '//core/ajax/translations.php', '/index.php'), - array('/core/ajax/translations.php', '/oc/core/ajax/translations.php', '/oc/index.php'), - array('/core/ajax/translations.php', '/oc//index.php/core/ajax/translations.php', '/oc/index.php'), - array('/1', '/oc/core/1', '/oc/core/index.php'), - ); - } - - /** - * @dataProvider rawPathInfoThrowsExceptionProvider - * @expectedException Exception - * - * @param $requestUri - * @param $scriptName - */ - public function testRawPathInfoThrowsException($requestUri, $scriptName) { - $_SERVER['REQUEST_URI'] = $requestUri; - $_SERVER['SCRIPT_NAME'] = $scriptName; - OC_Request::getRawPathInfo(); - } - - function rawPathInfoThrowsExceptionProvider() { - return array( - array('/oc/core1', '/oc/core/index.php'), - ); - } - - /** - * @dataProvider userAgentProvider - */ - public function testUserAgent($testAgent, $userAgent, $matches) { - $_SERVER['HTTP_USER_AGENT'] = $testAgent; - $this->assertEquals($matches, OC_Request::isUserAgent($userAgent)); - } - - function userAgentProvider() { - return array( - array( - 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)', - OC_Request::USER_AGENT_IE, - true - ), - array( - 'Mozilla/5.0 (X11; Linux i686; rv:24.0) Gecko/20100101 Firefox/24.0', - OC_Request::USER_AGENT_IE, - false - ), - array( - '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', - OC_Request::USER_AGENT_ANDROID_MOBILE_CHROME, - true - ), - array( - 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)', - OC_Request::USER_AGENT_ANDROID_MOBILE_CHROME, - false - ), - // test two values - array( - 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)', - array( - OC_Request::USER_AGENT_IE, - OC_Request::USER_AGENT_ANDROID_MOBILE_CHROME, - ), - true - ), - array( - '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', - array( - OC_Request::USER_AGENT_IE, - OC_Request::USER_AGENT_ANDROID_MOBILE_CHROME, - ), - true - ), - array( - 'Mozilla/5.0 (X11; Linux i686; rv:24.0) Gecko/20100101 Firefox/24.0', - OC_Request::USER_AGENT_FREEBOX, - false - ), - array( - 'Mozilla/5.0', - OC_Request::USER_AGENT_FREEBOX, - true - ), - array( - 'Fake Mozilla/5.0', - OC_Request::USER_AGENT_FREEBOX, - false - ), - ); - } - - public function testInsecureServerHost() { - unset($_SERVER['HTTP_X_FORWARDED_HOST']); - unset($_SERVER['HTTP_HOST']); - unset($_SERVER['SERVER_NAME']); - $_SERVER['SERVER_NAME'] = 'from.server.name:8080'; - $host = OC_Request::insecureServerHost(); - $this->assertEquals('from.server.name:8080', $host); - - $_SERVER['HTTP_HOST'] = 'from.host.header:8080'; - $host = OC_Request::insecureServerHost(); - $this->assertEquals('from.host.header:8080', $host); - - $_SERVER['HTTP_X_FORWARDED_HOST'] = 'from.forwarded.host:8080'; - $host = OC_Request::insecureServerHost(); - $this->assertEquals('from.forwarded.host:8080', $host); - - $_SERVER['HTTP_X_FORWARDED_HOST'] = 'from.forwarded.host2:8080,another.one:9000'; - $host = OC_Request::insecureServerHost(); - $this->assertEquals('from.forwarded.host2:8080', $host); - - // clean up - unset($_SERVER['HTTP_X_FORWARDED_HOST']); - unset($_SERVER['HTTP_HOST']); - unset($_SERVER['SERVER_NAME']); - } - - public function testGetOverwriteHost() { - unset($_SERVER['REMOTE_ADDR']); - OC_Config::deleteKey('overwritecondaddr'); - OC_Config::deleteKey('overwritehost'); - $host = OC_Request::getOverwriteHost(); - $this->assertNull($host); - - OC_Config::setValue('overwritehost', ''); - $host = OC_Request::getOverwriteHost(); - $this->assertNull($host); - - OC_Config::setValue('overwritehost', 'host.one.test:8080'); - $host = OC_Request::getOverwriteHost(); - $this->assertEquals('host.one.test:8080', $host); - - $_SERVER['REMOTE_ADDR'] = 'somehost.test:8080'; - OC_Config::setValue('overwritecondaddr', '^somehost\..*$'); - $host = OC_Request::getOverwriteHost(); - $this->assertEquals('host.one.test:8080', $host); - - OC_Config::setValue('overwritecondaddr', '^somethingelse.*$'); - $host = OC_Request::getOverwriteHost(); - $this->assertNull($host); - - // clean up - unset($_SERVER['REMOTE_ADDR']); - OC_Config::deleteKey('overwritecondaddr'); - OC_Config::deleteKey('overwritehost'); - } - - public function hostWithPortProvider() { - return array( - array('localhost:500', 'localhost'), - array('foo.com', 'foo.com'), - array('[1fff:0:a88:85a3::ac1f]:801', '[1fff:0:a88:85a3::ac1f]'), - array('[1fff:0:a88:85a3::ac1f]', '[1fff:0:a88:85a3::ac1f]') - ); - } - - /** - * @dataProvider hostWithPortProvider - */ - public function testGetDomainWithoutPort($hostWithPort, $host) { - $this->assertEquals($host, OC_Request::getDomainWithoutPort($hostWithPort)); - - } - - /** - * @dataProvider trustedDomainDataProvider - */ - public function testIsTrustedDomain($trustedDomains, $testDomain, $result) { - OC_Config::deleteKey('trusted_domains'); - if ($trustedDomains !== null) { - OC_Config::setValue('trusted_domains', $trustedDomains); - } - - $this->assertEquals($result, OC_Request::isTrustedDomain($testDomain)); - - // clean up - OC_Config::deleteKey('trusted_domains'); - } - - public function trustedDomainDataProvider() { - $trustedHostTestList = array('host.one.test', 'host.two.test', '[1fff:0:a88:85a3::ac1f]'); - return array( - // empty defaults to true - array(null, 'host.one.test:8080', true), - array('', 'host.one.test:8080', true), - array(array(), 'host.one.test:8080', true), - - // trust list when defined - array($trustedHostTestList, 'host.two.test:8080', true), - array($trustedHostTestList, 'host.two.test:9999', true), - array($trustedHostTestList, 'host.three.test:8080', false), - array($trustedHostTestList, 'host.two.test:8080:aa:222', false), - array($trustedHostTestList, '[1fff:0:a88:85a3::ac1f]', true), - array($trustedHostTestList, '[1fff:0:a88:85a3::ac1f]:801', true), - array($trustedHostTestList, '[1fff:0:a88:85a3::ac1f]:801:34', false), - - // trust localhost regardless of trust list - array($trustedHostTestList, 'localhost', true), - array($trustedHostTestList, 'localhost:8080', true), - array($trustedHostTestList, '127.0.0.1', true), - array($trustedHostTestList, '127.0.0.1:8080', true), - - // do not trust invalid localhosts - array($trustedHostTestList, 'localhost:1:2', false), - array($trustedHostTestList, 'localhost: evil.host', false), - ); - } - - public function testServerHost() { - OC_Config::deleteKey('overwritecondaddr'); - OC_Config::setValue('overwritehost', 'overwritten.host:8080'); - OC_Config::setValue( - 'trusted_domains', - array( - 'trusted.host:8080', - 'second.trusted.host:8080' - ) - ); - $_SERVER['HTTP_HOST'] = 'trusted.host:8080'; - - // CLI always gives localhost - $oldCLI = OC::$CLI; - OC::$CLI = true; - $host = OC_Request::serverHost(); - $this->assertEquals('localhost', $host); - OC::$CLI = false; - - // overwritehost overrides trusted domain - $host = OC_Request::serverHost(); - $this->assertEquals('overwritten.host:8080', $host); - - // trusted domain returned when used - OC_Config::deleteKey('overwritehost'); - $host = OC_Request::serverHost(); - $this->assertEquals('trusted.host:8080', $host); - - // trusted domain returned when untrusted one in header - $_SERVER['HTTP_HOST'] = 'untrusted.host:8080'; - OC_Config::deleteKey('overwritehost'); - $host = OC_Request::serverHost(); - $this->assertEquals('trusted.host:8080', $host); - - // clean up - OC_Config::deleteKey('overwritecondaddr'); - OC_Config::deleteKey('overwritehost'); - unset($_SERVER['HTTP_HOST']); - OC::$CLI = $oldCLI; - } -} diff --git a/tests/lib/security/trusteddomainhelper.php b/tests/lib/security/trusteddomainhelper.php new file mode 100644 index 0000000000..c8d5ffa587 --- /dev/null +++ b/tests/lib/security/trusteddomainhelper.php @@ -0,0 +1,70 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +use \OC\Security\TrustedDomainHelper; +use OCP\IConfig; + +/** + * Class TrustedDomainHelperTest + */ +class TrustedDomainHelperTest extends \Test\TestCase { + /** @var IConfig */ + protected $config; + + protected function setUp() { + parent::setUp(); + + $this->config = $this->getMockBuilder('\OCP\IConfig')->getMock(); + } + + /** + * @dataProvider trustedDomainDataProvider + * @param string $trustedDomains + * @param string $testDomain + * @param bool $result + */ + public function testIsTrustedDomain($trustedDomains, $testDomain, $result) { + $this->config->expects($this->once()) + ->method('getSystemValue') + ->with('trusted_domains') + ->will($this->returnValue($trustedDomains)); + + $trustedDomainHelper = new TrustedDomainHelper($this->config); + $this->assertEquals($result, $trustedDomainHelper->isTrustedDomain($testDomain)); + } + + /** + * @return array + */ + public function trustedDomainDataProvider() { + $trustedHostTestList = ['host.one.test', 'host.two.test', '[1fff:0:a88:85a3::ac1f]']; + return [ + // empty defaults to false with 8.1 + [null, 'host.one.test:8080', false], + ['', 'host.one.test:8080', false], + [[], 'host.one.test:8080', false], + // trust list when defined + [$trustedHostTestList, 'host.two.test:8080', true], + [$trustedHostTestList, 'host.two.test:9999', true], + [$trustedHostTestList, 'host.three.test:8080', false], + [$trustedHostTestList, 'host.two.test:8080:aa:222', false], + [$trustedHostTestList, '[1fff:0:a88:85a3::ac1f]', true], + [$trustedHostTestList, '[1fff:0:a88:85a3::ac1f]:801', true], + [$trustedHostTestList, '[1fff:0:a88:85a3::ac1f]:801:34', false], + // trust localhost regardless of trust list + [$trustedHostTestList, 'localhost', true], + [$trustedHostTestList, 'localhost:8080', true], + [$trustedHostTestList, '127.0.0.1', true], + [$trustedHostTestList, '127.0.0.1:8080', true], + // do not trust invalid localhosts + [$trustedHostTestList, 'localhost:1:2', false], + [$trustedHostTestList, 'localhost: evil.host', false], + ]; + } + +} diff --git a/tests/lib/templatelayout.php b/tests/lib/templatelayout.php index 1035dae122..c23aaa9b76 100644 --- a/tests/lib/templatelayout.php +++ b/tests/lib/templatelayout.php @@ -52,7 +52,7 @@ class OC_TemplateLayout extends \Test\TestCase { */ public function testConvertToRelativePath($absolutePath, $expected) { $_SERVER['REQUEST_URI'] = $expected; - $_SERVER['SCRIPT_NAME'] = '/'; + $_SERVER['SCRIPT_NAME'] = $expected; $relativePath = \Test_Helper::invokePrivate(new \OC_TemplateLayout('user'), 'convertToRelativePath', array($absolutePath)); $this->assertEquals($expected, $relativePath);