diff --git a/lib/SimpleSAML/Utils/HTTP.php b/lib/SimpleSAML/Utils/HTTP.php index de6149f390ac6bc35dcbb79a75f8fafde91ea4e8..25d5596e951c163c906be43c89682fcdd7fcab2a 100644 --- a/lib/SimpleSAML/Utils/HTTP.php +++ b/lib/SimpleSAML/Utils/HTTP.php @@ -711,7 +711,13 @@ class HTTP /** - * Retrieve the current URL using the base URL in the configuration. + * Retrieve the current URL using the base URL in the configuration, if possible. + * + * This method will try to see if the current script is part of SimpleSAMLphp. In that case, it will use the + * 'baseurlpath' configuration option to rebuild the current URL based on that. If the current script is NOT + * part of SimpleSAMLphp, it will just return the current URL. + * + * Note that this method does NOT make use of the HTTP X-Forwarded-* set of headers. * * @return string The current URL, including query parameters. * @@ -721,14 +727,24 @@ class HTTP */ public static function getSelfURL() { - $url = self::getBaseURL(); $cfg = \SimpleSAML_Configuration::getInstance(); $baseDir = $cfg->getBaseDir(); - $rel_path = str_replace( - DIRECTORY_SEPARATOR, - '/', - str_replace($baseDir.'www'.DIRECTORY_SEPARATOR, '', realpath($_SERVER['SCRIPT_FILENAME'])) - ); + $current_path = realpath($_SERVER['SCRIPT_FILENAME']); + $rel_path = str_replace($baseDir.'www'.DIRECTORY_SEPARATOR, '', $current_path); + + if ($current_path == $rel_path) { // compare loosely ($current_path can be false) + // we were accessed from an external script, do not try to apply our base URL + $protocol = 'http'; + $protocol .= (self::getServerHTTPS()) ? 's' : ''; + $protocol .= '://'; + + $hostname = self::getServerHost(); + $port = self::getServerPort(); + return $protocol.$hostname.$port.$_SERVER['REQUEST_URI']; + } + + $url = self::getBaseURL(); + $rel_path = str_replace(DIRECTORY_SEPARATOR, '/', $rel_path); $pos = strpos($_SERVER['REQUEST_URI'], $rel_path) + strlen($rel_path); return $url.$rel_path.substr($_SERVER['REQUEST_URI'], $pos); } @@ -745,7 +761,7 @@ class HTTP */ public static function getSelfURLHost() { - $url = self::getBaseURL(); + $url = self::getSelfURL(); $start = strpos($url, '://') + 3; $length = strcspn($url, '/', $start) + $start; return substr($url, 0, $length); @@ -781,7 +797,7 @@ class HTTP */ public static function isHTTPS() { - return strpos(self::getBaseURL(), 'https://') === 0; + return strpos(self::getSelfURL(), 'https://') === 0; } diff --git a/tests/lib/SimpleSAML/Utils/HTTPTest.php b/tests/lib/SimpleSAML/Utils/HTTPTest.php index 92a022fe4fb8b1068d635c3a8c875dc305b08324..b6bfb6b160016b7093289a9e582d1cb9adae031e 100644 --- a/tests/lib/SimpleSAML/Utils/HTTPTest.php +++ b/tests/lib/SimpleSAML/Utils/HTTPTest.php @@ -6,6 +6,31 @@ use SimpleSAML\Utils\HTTP; class HTTPTest extends \PHPUnit_Framework_TestCase { + + /** + * Set up the environment ($_SERVER) populating the typical variables from a given URL. + * + * @param string $url The URL to use as the current one. + */ + private function setupEnvFromURL($url) + { + $addr = parse_url($url); + $_SERVER['HTTP_HOST'] = $addr['host']; + $_SERVER['SERVER_NAME'] = $addr['host']; + if ($addr['scheme'] === 'https') { + $_SERVER['HTTPS'] = 'on'; + $default_port = '443'; + } else { + unset($_SERVER['HTTPS']); + $default_port = '80'; + } + $_SERVER['SERVER_PORT'] = $default_port; + if (isset($addr['port']) && strval($addr['port']) !== $default_port) { + $_SERVER['SERVER_PORT'] = strval($addr['port']); + } + $_SERVER['REQUEST_URI'] = $addr['path'].'?'.$addr['query']; + } + /** * Test SimpleSAML\Utils\HTTP::addURLParameters(). * @@ -144,71 +169,112 @@ class HTTPTest extends \PHPUnit_Framework_TestCase /** * Test SimpleSAML\Utils\HTTP::getSelfURL(). */ - public function testGetSelfURL() + public function testGetSelfURLMethods() { $original = $_SERVER; + /* + * Test a URL pointing to a script that's not part of the public interface. This allows us to test calls to + * getSelfURL() from scripts outside of SimpleSAMLphp + */ + \SimpleSAML_Configuration::loadFromArray(array( + 'baseurlpath' => 'http://example.com/simplesaml/', + ), '[ARRAY]', 'simplesaml'); + $url = 'https://example.com/app/script.php/some/path?foo=bar'; + $this->setupEnvFromURL($url); + $_SERVER['SCRIPT_FILENAME'] = '/var/www/app/script.php'; + $this->assertEquals($url, HTTP::getSelfURL()); + $this->assertEquals('https://example.com', HTTP::getSelfURLHost()); + $this->assertEquals('https://example.com/app/script.php/some/path', HTTP::getSelfURLNoQuery()); + $this->assertTrue(HTTP::isHTTPS()); + $this->assertEquals('https://'.HTTP::getSelfHostWithNonStandardPort(), HTTP::getSelfURLHost()); + // test a valid, full URL, based on a full URL in the configuration $cfg = \SimpleSAML_Configuration::loadFromArray(array( 'baseurlpath' => 'https://example.com/simplesaml/', ), '[ARRAY]', 'simplesaml'); $baseDir = $cfg->getBaseDir(); $_SERVER['SCRIPT_FILENAME'] = $baseDir.'www/module.php'; - $_SERVER['REQUEST_URI'] = '/simplesaml/module.php/module/file.php?foo=bar#something'; + $this->setupEnvFromURL('http://www.example.org/module.php/module/file.php?foo=bar'); $this->assertEquals( - 'https://example.com/simplesaml/module.php/module/file.php?foo=bar#something', + 'https://example.com/simplesaml/module.php/module/file.php?foo=bar', HTTP::getSelfURL() ); + $this->assertEquals('https://example.com', HTTP::getSelfURLHost()); + $this->assertEquals('https://example.com/simplesaml/module.php/module/file.php', HTTP::getSelfURLNoQuery()); + $this->assertTrue(HTTP::isHTTPS()); + $this->assertEquals('https://'.HTTP::getSelfHostWithNonStandardPort(), HTTP::getSelfURLHost()); // test a valid, full URL, based on a full URL *without* a trailing slash in the configuration \SimpleSAML_Configuration::loadFromArray(array( 'baseurlpath' => 'https://example.com/simplesaml', ), '[ARRAY]', 'simplesaml'); $this->assertEquals( - 'https://example.com/simplesaml/module.php/module/file.php?foo=bar#something', + 'https://example.com/simplesaml/module.php/module/file.php?foo=bar', HTTP::getSelfURL() ); + $this->assertEquals('https://example.com', HTTP::getSelfURLHost()); + $this->assertEquals('https://example.com/simplesaml/module.php/module/file.php', HTTP::getSelfURLNoQuery()); + $this->assertTrue(HTTP::isHTTPS()); + $this->assertEquals('https://'.HTTP::getSelfHostWithNonStandardPort(), HTTP::getSelfURLHost()); // test a valid, full URL, based on a full URL *without* a path in the configuration \SimpleSAML_Configuration::loadFromArray(array( 'baseurlpath' => 'https://example.com', ), '[ARRAY]', 'simplesaml'); $this->assertEquals( - 'https://example.com/module.php/module/file.php?foo=bar#something', + 'https://example.com/module.php/module/file.php?foo=bar', HTTP::getSelfURL() ); + $this->assertEquals('https://example.com', HTTP::getSelfURLHost()); + $this->assertEquals('https://example.com/module.php/module/file.php', HTTP::getSelfURLNoQuery()); + $this->assertTrue(HTTP::isHTTPS()); + $this->assertEquals('https://'.HTTP::getSelfHostWithNonStandardPort(), HTTP::getSelfURLHost()); // test a valid, full URL, based on a relative path in the configuration \SimpleSAML_Configuration::loadFromArray(array( 'baseurlpath' => '/simplesaml/', ), '[ARRAY]', 'simplesaml'); - $_SERVER['HTTP_HOST'] = 'example.org'; - unset($_SERVER['HTTPS']); - unset($_SERVER['SERVER_PORT']); + $this->setupEnvFromURL('http://www.example.org/simplesaml/module.php/module/file.php?foo=bar'); $this->assertEquals( - 'http://example.org/simplesaml/module.php/module/file.php?foo=bar#something', + 'http://www.example.org/simplesaml/module.php/module/file.php?foo=bar', HTTP::getSelfURL() ); + $this->assertEquals('http://www.example.org', HTTP::getSelfURLHost()); + $this->assertEquals('http://www.example.org/simplesaml/module.php/module/file.php', HTTP::getSelfURLNoQuery()); + $this->assertFalse(HTTP::isHTTPS()); + $this->assertEquals('http://'.HTTP::getSelfHostWithNonStandardPort(), HTTP::getSelfURLHost()); // test a valid, full URL, based on a relative path in the configuration and a non standard port \SimpleSAML_Configuration::loadFromArray(array( 'baseurlpath' => '/simplesaml/', ), '[ARRAY]', 'simplesaml'); - $_SERVER['SERVER_PORT'] = '8080'; + $this->setupEnvFromURL('http://example.org:8080/simplesaml/module.php/module/file.php?foo=bar'); $this->assertEquals( - 'http://example.org:8080/simplesaml/module.php/module/file.php?foo=bar#something', + 'http://example.org:8080/simplesaml/module.php/module/file.php?foo=bar', HTTP::getSelfURL() ); + $this->assertEquals('http://example.org:8080', HTTP::getSelfURLHost()); + $this->assertEquals('http://example.org:8080/simplesaml/module.php/module/file.php', HTTP::getSelfURLNoQuery()); + $this->assertFalse(HTTP::isHTTPS()); + $this->assertEquals('http://'.HTTP::getSelfHostWithNonStandardPort(), HTTP::getSelfURLHost()); // test a valid, full URL, based on a relative path in the configuration, a non standard port and HTTPS \SimpleSAML_Configuration::loadFromArray(array( 'baseurlpath' => '/simplesaml/', ), '[ARRAY]', 'simplesaml'); - $_SERVER['HTTPS'] = 'on'; + $this->setupEnvFromURL('https://example.org:8080/simplesaml/module.php/module/file.php?foo=bar'); $this->assertEquals( - 'https://example.org:8080/simplesaml/module.php/module/file.php?foo=bar#something', + 'https://example.org:8080/simplesaml/module.php/module/file.php?foo=bar', HTTP::getSelfURL() ); + $this->assertEquals('https://example.org:8080', HTTP::getSelfURLHost()); + $this->assertEquals( + 'https://example.org:8080/simplesaml/module.php/module/file.php', + HTTP::getSelfURLNoQuery() + ); + $this->assertTrue(HTTP::isHTTPS()); + $this->assertEquals('https://'.HTTP::getSelfHostWithNonStandardPort(), HTTP::getSelfURLHost()); $_SERVER = $original; } @@ -234,8 +300,7 @@ class HTTPTest extends \PHPUnit_Framework_TestCase 'https://app.example.com/', 'http://app.example.com/', ); - foreach ($allowed as $url) - { + foreach ($allowed as $url) { $this->assertEquals(HTTP::checkURLAllowed($url), $url); } @@ -267,8 +332,7 @@ class HTTPTest extends \PHPUnit_Framework_TestCase 'https://app2.example.com/', 'http://app2.example.com/', ); - foreach ($allowed as $url) - { + foreach ($allowed as $url) { $this->assertEquals(HTTP::checkURLAllowed($url), $url); }