Skip to content
Snippets Groups Projects
Commit e8ee8c83 authored by Jaime Pérez's avatar Jaime Pérez
Browse files

bugfix: Restore the capability to get our self URL when invoked from a third-party script.

Recent fixes for URL guessing and building addressed bugs in the code that were preventing the 'baseurlpath' from being used properly. However, they introduced a new issue, as the code was assuming the current URL would always point to a SimpleSAMLphp script. This is not always true, of course, as any script can invoke our API and end up trying to get its own URL (for example, when calling requireAuth()).

In order to fix this, we monitor mismatches between SimpleSAMLphp's installation path and the absolute, real path to the current script. When there's a mismatch, it means we are running a third-party script outside SimpleSAMLphp, and therefore we should NOT enforce 'baseurlpath'. This introduces an additional issue, as applications behind a reverse proxy may cause trouble to guess the right URL (we will use the URL as seen by SimpleSAMLphp in the server, which is not necessarily the same as the user sees with a reverse proxy in between). For the moment, we'll leave the responsibility to sort that issue out to implementors. It might be a good idea to add a page to the wiki explaining how to do this.

This resolves #418.
parent e6fffdcb
No related merge requests found
......@@ -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;
}
......
......@@ -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);
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment