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-73492969remotes/origin/log-external-deletes
parent
7f624188a7
commit
886bda5f81
@ -1,330 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl>
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) 2015 Lukas Reschke <lukas@owncloud.com>
|
||||
* This file is licensed under the Affero General Public License version 3 or
|
||||
* later.
|
||||
* See the COPYING-README file.
|
||||
*/
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,333 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) 2013 Thomas Müller <thomas.mueller@tmit.eu>
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) 2015 Lukas Reschke <lukas@owncloud.com>
|
||||
* This file is licensed under the Affero General Public License version 3 or
|
||||
* later.
|
||||
* See the COPYING-README file.
|
||||
*/
|
||||
|
||||
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],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue