New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow overriding of host, port, protocol nsdr url path for URL building #175

Merged
merged 6 commits into from Nov 15, 2016
View
@@ -279,6 +279,12 @@ $settings = array (
// Enable debug mode (to print errors).
'debug' => false,
// Set a BaseURL to be used instead of try to guess
// the BaseURL of the view that process the SAML Message.
// Ex http://sp.example.com/
// http://example.com/sp/
'baseurl' => null,
// Service Provider Data that we are deploying.
'sp' => array (
// Identifier of the SP entity (must be a URI)
@@ -1035,6 +1041,26 @@ if (isset($_SESSION['samlUserdata'])) { // If there is user data we print it.
}
```
#### URL-guessing methods ####
php-saml toolkit uses a bunch of methods in OneLogin_Saml2_Utils that try to guess the URL where the SAML messages are processed.
* `getSelfHost` Returns the current host.
* `getSelfPort` Return the port number used for the request
* `isHTTPS` Checks if the protocol is https or http.
* `getSelfURLhost` Returns the protocol + the current host + the port (if different than common ports).
* `getSelfURL` Returns the URL of the current host + current view + query.
* `getSelfURLNoQuery` Returns the URL of the current host + current view.
* `getSelfRoutedURLNoQuery` Returns the routed URL of the current host + current view.
getSelfURLNoQuery and getSelfRoutedURLNoQuery are used to calculate the currentURL in order to valdate SAML elements like Destination or Recipient.
When the PHP application is behind a proxy or a load balancer we can execute setProxyVars(true) and getSelfPort and isHTTPS will take care of the $_SERVER["HTTP_X_FORWARDED_PORT"] and $_SERVER['HTTP_X_FORWARDED_PROTO'] vars (otherwise they are ignored).
Also a developer can use setSelfProtocol, setSelfHost, setSelfPort and getBaseURLPath to define a specific value to be returned by isHTTPS, getSelfHost, getSelfPort and getBaseURLPath. And define a setBasePath to be used on the getSelfURL and getSelfRoutedURLNoQuery to replace the data extracted from $_SERVER["REQUEST_URI"].
At the settings the developer will be able to set a 'baseurl' parameter that automatically will use setBaseURL to set values for setSelfProtocol, setSelfHost, setSelfPort and setBaseURLPath.
### Main classes and methods ###
Described below are the main classes and methods that can be invoked.
@@ -1196,7 +1222,9 @@ Configuration of the OneLogin PHP Toolkit
* `formatSPKey` - Formats the SP private key.
* `getErrors` - Returns an array with the errors, the array is empty when
the settings is ok.
* `getLastErrorReason`* Returns the reason of the last error
* `getLastErrorReason` - Returns the reason of the last error
* `getBaseURL` - Returns the baseurl set on the settings if any.
* `setBaseURL` - Set a baseurl value
* `setStrict` - Activates or deactivates the strict mode.
* `isStrict` - Returns if the 'strict' mode is active.
* `isDebugActive` - Returns if the debug is active.
@@ -41,9 +41,13 @@ class OneLogin_Saml2_LogoutRequest
*/
public function __construct(OneLogin_Saml2_Settings $settings, $request = null, $nameId = null, $sessionIndex = null)
{
$this->_settings = $settings;
if (!empty($this->_settings->getBaseURL())) {
$baseURL = $this->_settings->getBaseURL();
OneLogin_Saml2_Utils::setBaseURL($baseURL);
}
if (!isset($request) || empty($request)) {
$spData = $this->_settings->getSPData();
@@ -41,6 +41,12 @@ class OneLogin_Saml2_LogoutResponse
public function __construct(OneLogin_Saml2_Settings $settings, $response = null)
{
$this->_settings = $settings;
if (!empty($this->_settings->getBaseURL())) {
$baseURL = $this->_settings->getBaseURL();
OneLogin_Saml2_Utils::setBaseURL($baseURL);
}
if ($response) {
$decoded = base64_decode($response);
$inflated = @gzinflate($decoded);
View
@@ -56,6 +56,11 @@ public function __construct(OneLogin_Saml2_Settings $settings, $response)
{
$this->_settings = $settings;
if (!empty($this->_settings->getBaseURL())) {
$baseURL = $this->_settings->getBaseURL();
OneLogin_Saml2_Utils::setBaseURL($baseURL);
}
$this->response = base64_decode($response);
$this->document = new DOMDocument();
View
@@ -14,6 +14,11 @@ class OneLogin_Saml2_Settings
*/
private $_paths = array();
/**
* @var string
*/
private $_baseurl;
/**
* Strict. If active, PHP Toolkit will reject unsigned or unencrypted messages
* if it expects them signed or encrypted. If not, the messages will be accepted
@@ -240,6 +245,10 @@ private function _loadSettingsFromArray($settings)
$this->_debug = $settings['debug'];
}
if (isset($settings['baseurl'])) {
$this->_baseurl = $settings['baseurl'];
}
if (isset($settings['compress'])) {
$this->_compress = $settings['compress'];
}
@@ -940,6 +949,24 @@ public function isDebugActive()
return $this->_debug;
}
/**
* Set a baseurl value.
*/
public function setBaseURL($baseurl)
{
$this->_baseurl = $baseurl;
}
/**
* Returns the baseurl set on the settings if any.
*
* @return null|string The baseurl
*/
public function getBaseURL()
{
return $this->_baseurl;
}
/**
* Sets the IdP certificate.
*
View
@@ -32,6 +32,12 @@ class OneLogin_Saml2_Utils
*/
private static $_port;
/**
* @var string
*/
private static $_baseurlpath;
/**
* Translates any string. Accepts args
*
@@ -249,7 +255,7 @@ public static function redirect($url, $parameters = array(), $stay = false)
}
/* Verify that the URL is to a http or https site. */
if (!preg_match('@^https?://@i', $url)) {
if (!preg_match('@^https?:\/\/@i', $url)) {
throw new OneLogin_Saml2_Error(
'Redirect to invalid URL: ' . $url,
OneLogin_Saml2_Error::REDIRECT_INVALID_URL
@@ -296,6 +302,41 @@ public static function redirect($url, $parameters = array(), $stay = false)
exit();
}
/**
* @param $baseurl string The base url to be used when constructing URLs
*/
public static function setBaseURL($baseurl)
{
if (!empty($baseurl)) {
$baseurlpath = '/';
if (preg_match('#^https?:\/\/([^\/]*)\/?(.*)#i', $baseurl, $matches)) {
if (strpos($baseurl, 'https://') == False) {

This comment has been minimized.

@dhensby

dhensby Nov 13, 2016

Contributor

this needs to be type equality (===) and you should use false not False

This comment has been minimized.

@pitbulk

pitbulk Nov 13, 2016

Contributor

Sure

self::setSelfProtocol('http');
$port = '80';
} else {
self::setSelfProtocol('https');
$port = '443';
}
$currentHost = $matches[1];
if (false !== strpos($currentHost, ':')) {
list($currentHost, $possiblePort) = explode(':', $matches[1], 2);
if (is_numeric($possiblePort)) {
$port = $possiblePort;
}
}
if (isset($matches[2]) && !empty($matches[2])) {
$baseurlpath = $matches[2];
}
self::setSelfHost($currentHost);
self::setSelfPort($port);
self::setBaseURLPath($baseurlpath);
}
}
}
/**
* @param $proxyVars bool Whether to use `X-Forwarded-*` headers to determine port/domain/protocol
*/
@@ -347,6 +388,26 @@ public static function setSelfHost($host)
self::$_host = $host;
}
/**
* @param $baseurlpath string The baseurl path to use when constructing URLs
*/
public static function setBaseURLPath($baseurlpath)
{
if (!isset($baseurlpath) || empty($baseurlpath)) {

This comment has been minimized.

@dhensby

dhensby Nov 13, 2016

Contributor

you can just used empty as isset will only return false if $baseurlpath is explicitly null, but empty will return true for null anyway.

This comment has been minimized.

@pitbulk

pitbulk Nov 13, 2016

Contributor

Ok

$baseurlpath = '/';
}
self::$_baseurlpath = '/'. ltrim(rtrim($baseurlpath, '/') . '/', '/');

This comment has been minimized.

@dhensby

dhensby Nov 13, 2016

Contributor

Could you not do '/'.trim($baseurlpath, '/') . '/';

This comment has been minimized.

@pitbulk

pitbulk Nov 13, 2016

Contributor

I wanted to assure that all start and end with a /. What the reason for avoiding that?

This comment has been minimized.

@dhensby

dhensby Nov 14, 2016

Contributor

Won't my suggestion have the same effect? Trim / off both ends then spend it to both?

This comment has been minimized.

@pitbulk

pitbulk Nov 14, 2016

Contributor

haha, yes, sorry, yesterday was so late when I reviewed that

This comment has been minimized.

@pitbulk

pitbulk Nov 14, 2016

Contributor

Ohh, when $baseurlpath ==> '/'

'/'.trim($baseurlpath, '/') . '/'; returns //
but '/'. ltrim(rtrim($baseurlpath, '/') . '/', '/'); returns /

I will use

        if (empty($baseurlpath) || $baseurlpath == '/') {
            $baseurlpath = '/';
        } else {
            self::$_baseurlpath = '/' . trim($baseurlpath, '/') . '/';
        }

This comment has been minimized.

@dhensby

dhensby Nov 14, 2016

Contributor

Ah, good point and that solution is more readable too, so cool.

}
/**
* return string The baseurlpath to be used when constructing URLs
*/
public static function getBaseURLPath()
{
return self::$_baseurlpath;
}
/**
* @return string The raw host name
*/
@@ -464,9 +525,19 @@ public static function isHTTPS()
*/
public static function getSelfURLNoQuery()
{
$selfURLhost = self::getSelfURLhost();
$selfURLNoQuery = $selfURLhost . $_SERVER['SCRIPT_NAME'];
if (!empty(self::getBaseURLPath())) {

This comment has been minimized.

@dhensby

dhensby Nov 13, 2016

Contributor

you can't use empty with method in php < 5.5 so you need to assign a var first as the module supports 5.3+

This comment has been minimized.

@pitbulk

pitbulk Nov 13, 2016

Contributor

Sure, I noticed that on travis

$path = explode('/', $_SERVER['SCRIPT_NAME']);
$selfURLNoQuery = $selfURLhost . self::getBaseURLPath();
$script = array_pop($path);
if (!empty($script)) {
$selfURLNoQuery .= $script;
}
} else {
$selfURLNoQuery = $selfURLhost . $_SERVER['SCRIPT_NAME'];
}
if (isset($_SERVER['PATH_INFO'])) {
$selfURLNoQuery .= $_SERVER['PATH_INFO'];
}
@@ -480,9 +551,9 @@ public static function getSelfURLNoQuery()
*/
public static function getSelfRoutedURLNoQuery()
{
$selfURLhost = self::getSelfURLhost();
$route = '';
if (!empty($_SERVER['REQUEST_URI'])) {
$route = $_SERVER['REQUEST_URI'];
if (!empty($_SERVER['QUERY_STRING'])) {
@@ -493,6 +564,17 @@ public static function getSelfRoutedURLNoQuery()
}
}
if (!empty(self::getBaseURLPath())) {

This comment has been minimized.

@dhensby

dhensby Nov 13, 2016

Contributor

This code is repeated and could be pulled into a protected method and re-used

This comment has been minimized.

@pitbulk

pitbulk Nov 13, 2016

Contributor

Noted

if (!empty($route)){
$path = explode('/', $route);
$route = self::getBaseURLPath();
$script = array_pop($path);
if (!empty($script)) {
$route .= $script;
}
}
}
$selfRoutedURLNoQuery = $selfURLhost . $route;
return $selfRoutedURLNoQuery;
}
@@ -510,11 +592,23 @@ public static function getSelfURL()
if (!empty($_SERVER['REQUEST_URI'])) {
$requestURI = $_SERVER['REQUEST_URI'];
if ($requestURI[0] !== '/') {
if (preg_match('#^https?://[^/]*(/.*)#i', $requestURI, $matches)) {
if (preg_match('#^https?:\/\/[^\/]*(\/.*)#i', $requestURI, $matches)) {
$requestURI = $matches[1];
}
}
}
if (!empty(self::getBaseURLPath())) {
if (!empty($requestURI)){
$path = explode('/', $requestURI);
$requestURI = self::getBaseURLPath();
$scriptAndQuery = array_pop($path);
if (!empty($scriptAndQuery)) {
$requestURI .= $scriptAndQuery;
}
}
}
return $selfURLhost . $requestURI;
}
View
@@ -10,6 +10,12 @@
// Enable debug mode (to print errors)
'debug' => false,
// Set a BaseURL to be used instead of try to guess
// the BaseURL of the view that process the SAML Message.
// Ex. http://sp.example.com/
// http://example.com/sp/
'baseurl' => null,
// Service Provider Data that we are deploying
'sp' => array (
// Identifier of the SP entity (must be a URI)
Oops, something went wrong.
ProTip! Use n and p to navigate between commits in a pull request.