diff options
author | Thomas Steur <thomas.steur@googlemail.com> | 2014-09-12 19:42:39 +0400 |
---|---|---|
committer | Thomas Steur <thomas.steur@googlemail.com> | 2014-09-12 19:42:39 +0400 |
commit | 19d5f67005fcb513253139ea149255c9ceb4320c (patch) | |
tree | 3de112844e749a241e62b81145cbf356f73767aa | |
parent | 0aea1092d32050dfb0b0f6791061536a6777acbc (diff) |
refs #4996 redirect only to trusted hosts (will have to remove this most likely again as you would have to register subdomains etc as well). Also added some missing test files
-rw-r--r-- | core/Tracker.php | 31 | ||||
-rw-r--r-- | core/Url.php | 40 | ||||
-rw-r--r-- | plugins/SitesManager/API.php | 37 | ||||
-rw-r--r-- | plugins/SitesManager/Model.php | 57 | ||||
-rw-r--r-- | tests/PHPUnit/Core/UrlTest.php | 50 | ||||
-rw-r--r-- | tests/PHPUnit/Integration/expected/test_ContentscontentNameOrPieceMatch__Contents.getContentNames_day.xml | 21 | ||||
-rw-r--r-- | tests/PHPUnit/Integration/expected/test_ContentscontentNameOrPieceMatch__Contents.getContentPieces_day.xml | 48 |
7 files changed, 245 insertions, 39 deletions
diff --git a/core/Tracker.php b/core/Tracker.php index d9e8a04278..a690a31225 100644 --- a/core/Tracker.php +++ b/core/Tracker.php @@ -10,6 +10,7 @@ namespace Piwik; use Exception; use Piwik\Plugins\PrivacyManager\Config as PrivacyManagerConfig; +use Piwik\Plugins\SitesManager\Model; use Piwik\Tracker\Cache; use Piwik\Tracker\Db\DbException; use Piwik\Tracker\Db\Mysqli; @@ -912,7 +913,6 @@ class Tracker private function getRedirectUrl() { - // TODO only redirecti if domain is trusted in config? return Common::getRequestVar('redirecturl', false, 'string'); } @@ -925,10 +925,33 @@ class Tracker private function performRedirectToUrlIfSet() { - if ($this->hasRedirectUrl()) { - $redirectUrl = $this->getRedirectUrl(); - header('Location: ' . $redirectUrl); + if (!$this->hasRedirectUrl()) { + return; + } + + $redirectUrl = $this->getRedirectUrl(); + $host = Url::getHostFromUrl($redirectUrl); + + if (empty($host)) { + return; + } + + $model = new Model(); + $siteIds = $model->getSitesId(); + + foreach ($siteIds as $siteId) { + $siteUrls = $model->getSiteUrlsFromId($siteId); + + if (Url::isHostInUrls($host, $siteUrls)) { + Url::redirectToUrl($redirectUrl); + } + } + + $trustedHosts = Url::getTrustedHosts(); + if (Url::isHostInUrls($host, $trustedHosts)) { + Url::redirectToUrl($redirectUrl); } } + } diff --git a/core/Url.php b/core/Url.php index 269c3ef6d1..3a52cc9806 100644 --- a/core/Url.php +++ b/core/Url.php @@ -598,4 +598,44 @@ class Url } return $hosts; } + + public static function getHostFromUrl($url) + { + $parsedUrl = parse_url($url); + + if (empty($parsedUrl['host'])) { + return; + } + + return Common::mb_strtolower($parsedUrl['host']); + } + + public static function isHostInUrls($host, $urls) + { + if (empty($host)) { + return false; + } + + $host = Common::mb_strtolower($host); + + if (!empty($urls)) { + foreach ($urls as $url) { + if (Common::mb_strtolower($url) === $host) { + return true; + } + + $siteHost = self::getHostFromUrl($url); + + if ($siteHost === $host) { + return true; + } + + if (Common::stringEndsWith($siteHost, $host)) { + return; + } + } + } + + return in_array($host, self::$alwaysTrustedHosts); + } } diff --git a/plugins/SitesManager/API.php b/plugins/SitesManager/API.php index 8eca58f47b..c65a9f4259 100644 --- a/plugins/SitesManager/API.php +++ b/plugins/SitesManager/API.php @@ -193,26 +193,6 @@ class API extends \Piwik\Plugin\API } /** - * Returns the list of alias URLs registered for the given idSite. - * The website ID must be valid when calling this method! - * - * @param int $idSite - * @return array list of alias URLs - */ - private function getAliasSiteUrlsFromId($idSite) - { - $db = Db::get(); - $result = $db->fetchAll("SELECT url - FROM " . Common::prefixTable("site_url") . " - WHERE idsite = ?", $idSite); - $urls = array(); - foreach ($result as $url) { - $urls[] = $url['url']; - } - return $urls; - } - - /** * Returns the list of all URLs registered for the given idSite (main_url + alias URLs). * * @throws Exception if the website ID doesn't exist or the user doesn't have access to it @@ -222,25 +202,12 @@ class API extends \Piwik\Plugin\API public function getSiteUrlsFromId($idSite) { Piwik::checkUserHasViewAccess($idSite); - $site = new Site($idSite); - $urls = $this->getAliasSiteUrlsFromId($idSite); - return array_merge(array($site->getMainUrl()), $urls); + return $this->getModel()->getSiteUrlsFromId($idSite); } - /** - * Returns the list of all the website IDs registered. - * Caller must check access. - * - * @return array The list of website IDs - */ private function getSitesId() { - $result = Db::fetchAll("SELECT idsite FROM " . Common::prefixTable('site')); - $idSites = array(); - foreach ($result as $idSite) { - $idSites[] = $idSite['idsite']; - } - return $idSites; + return $this->getModel()->getSitesId(); } /** diff --git a/plugins/SitesManager/Model.php b/plugins/SitesManager/Model.php index 9e77f48888..207f59bdfd 100644 --- a/plugins/SitesManager/Model.php +++ b/plugins/SitesManager/Model.php @@ -11,6 +11,7 @@ namespace Piwik\Plugins\SitesManager; use Piwik\Db; use Piwik\Common; use Exception; +use Piwik\Site; class Model { @@ -59,4 +60,60 @@ class Model return $site; } + + /** + * Returns the list of all the website IDs registered. + * Caller must check access. + * + * @return array The list of website IDs + */ + public function getSitesId() + { + $result = Db::fetchAll("SELECT idsite FROM " . Common::prefixTable('site')); + $idSites = array(); + foreach ($result as $idSite) { + $idSites[] = $idSite['idsite']; + } + return $idSites; + } + + /** + * Returns the list of all URLs registered for the given idSite (main_url + alias URLs). + * + * @throws Exception if the website ID doesn't exist or the user doesn't have access to it + * @param int $idSite + * @return array list of URLs + */ + public function getSiteUrlsFromId($idSite) + { + $urls = $this->getAliasSiteUrlsFromId($idSite); + + $site = $this->getSiteFromId($idSite); + + if (empty($site)) { + return $urls; + } + + return array_merge(array($site['main_url']), $urls); + } + + /** + * Returns the list of alias URLs registered for the given idSite. + * The website ID must be valid when calling this method! + * + * @param int $idSite + * @return array list of alias URLs + */ + public function getAliasSiteUrlsFromId($idSite) + { + $db = Db::get(); + $result = $db->fetchAll("SELECT url + FROM " . Common::prefixTable("site_url") . " + WHERE idsite = ?", $idSite); + $urls = array(); + foreach ($result as $url) { + $urls[] = $url['url']; + } + return $urls; + } } diff --git a/tests/PHPUnit/Core/UrlTest.php b/tests/PHPUnit/Core/UrlTest.php index fe4261854f..2620b9a5c9 100644 --- a/tests/PHPUnit/Core/UrlTest.php +++ b/tests/PHPUnit/Core/UrlTest.php @@ -280,4 +280,54 @@ class UrlTest extends PHPUnit_Framework_TestCase ); } + /** + * @group Core + * + * @dataProvider getHostsFromUrl + */ + public function testGetHostsFromUrl($url, $expectedHost) + { + $this->assertEquals($expectedHost, Url::getHostFromUrl($url)); + } + + public function getHostsFromUrl() + { + return array( + array(null, null), + array('http://', null), + array('http://www.example.com', 'www.example.com'), + array('http://www.ExaMplE.cOm', 'www.example.com'), + array('http://www.example.com/test/foo?bar=xy', 'www.example.com'), + array('http://127.0.0.1', '127.0.0.1'), + array('example.com', null), + ); + } + + /** + * @group Core + * + * @dataProvider getIsHostInUrls + */ + public function testIsHostInUrlsl($isHost, $host, $urls) + { + $this->assertEquals($isHost, Url::isHostInUrls($host, $urls)); + } + + public function getIsHostInUrls() + { + return array( + array(false, null, null), + array(false, 'http://', array()), + array(false, 'example.com', array()), + array(false, 'www.example.com', array()), + array(false, 'example.com', array('www.example.com')), + array(false, 'example.com', array('http://www.example.com')), + array(true, 'example.com', array('example.com')), + array(true, 'eXamPle.com', array('exaMple.com')), + array(true, 'eXamPle.com', array('http://exaMple.com')), + array(true, 'example.com', array('http://example.com/test')), + array(true, '127.0.0.1', array()), // always trusted host + ); + } + } diff --git a/tests/PHPUnit/Integration/expected/test_ContentscontentNameOrPieceMatch__Contents.getContentNames_day.xml b/tests/PHPUnit/Integration/expected/test_ContentscontentNameOrPieceMatch__Contents.getContentNames_day.xml new file mode 100644 index 0000000000..17b781db1c --- /dev/null +++ b/tests/PHPUnit/Integration/expected/test_ContentscontentNameOrPieceMatch__Contents.getContentNames_day.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8" ?> +<result> + <row> + <label>ImageAd</label> + <nb_uniq_visitors>8</nb_uniq_visitors> + <nb_visits>8</nb_visits> + <nb_impressions>8</nb_impressions> + <nb_interactions>2</nb_interactions> + <interaction_rate>25%</interaction_rate> + <contentTarget>http://www.example.com</contentTarget> + </row> + <row> + <label>Text Ad</label> + <nb_uniq_visitors>4</nb_uniq_visitors> + <nb_visits>4</nb_visits> + <nb_impressions>4</nb_impressions> + <nb_interactions>2</nb_interactions> + <interaction_rate>50%</interaction_rate> + <contentTarget>http://piwik.org/download</contentTarget> + </row> +</result>
\ No newline at end of file diff --git a/tests/PHPUnit/Integration/expected/test_ContentscontentNameOrPieceMatch__Contents.getContentPieces_day.xml b/tests/PHPUnit/Integration/expected/test_ContentscontentNameOrPieceMatch__Contents.getContentPieces_day.xml new file mode 100644 index 0000000000..7df6e4f0e1 --- /dev/null +++ b/tests/PHPUnit/Integration/expected/test_ContentscontentNameOrPieceMatch__Contents.getContentPieces_day.xml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="utf-8" ?> +<result> + <row> + <label>Click to download Piwik now</label> + <nb_uniq_visitors>4</nb_uniq_visitors> + <nb_visits>4</nb_visits> + <nb_impressions>4</nb_impressions> + <nb_interactions>2</nb_interactions> + <interaction_rate>50%</interaction_rate> + <contentTarget>http://piwik.org/download</contentTarget> + </row> + <row> + <label>/path/ad.jpg</label> + <nb_uniq_visitors>2</nb_uniq_visitors> + <nb_visits>2</nb_visits> + <nb_impressions>2</nb_impressions> + <nb_interactions>2</nb_interactions> + <interaction_rate>100%</interaction_rate> + <contentTarget>http://www.example.com</contentTarget> + </row> + <row> + <label>/path/ad2.jpg</label> + <nb_uniq_visitors>2</nb_uniq_visitors> + <nb_visits>2</nb_visits> + <nb_impressions>2</nb_impressions> + <nb_interactions>0</nb_interactions> + <interaction_rate>0%</interaction_rate> + <contentTarget>http://www.example.com</contentTarget> + </row> + <row> + <label>Content Piece not defined</label> + <nb_uniq_visitors>2</nb_uniq_visitors> + <nb_visits>2</nb_visits> + <nb_impressions>2</nb_impressions> + <nb_interactions>0</nb_interactions> + <interaction_rate>0%</interaction_rate> + <contentTarget /> + </row> + <row> + <label>Unknown</label> + <nb_uniq_visitors>2</nb_uniq_visitors> + <nb_visits>2</nb_visits> + <nb_impressions>2</nb_impressions> + <nb_interactions>0</nb_interactions> + <interaction_rate>0%</interaction_rate> + <contentTarget /> + </row> +</result>
\ No newline at end of file |