Skip to content

Commit

Permalink
Merge pull request #7520 from open-sausages/pulls/4.0/config-redirect…
Browse files Browse the repository at this point in the history
…-works

API / BUG - Introduce new request resolver middleware and fix broken forceWWW / forceSSL
  • Loading branch information
flamerohr authored Nov 1, 2017
2 parents 326f74e + 9d3277f commit cd55a03
Show file tree
Hide file tree
Showing 6 changed files with 609 additions and 121 deletions.
10 changes: 10 additions & 0 deletions _config/requestprocessors.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ SilverStripe\Core\Injector\Injector:
SessionMiddleware: '%$SilverStripe\Control\Middleware\SessionMiddleware'
RequestProcessorMiddleware: '%$SilverStripe\Control\RequestProcessor'
FlushMiddleware: '%$SilverStripe\Control\Middleware\FlushMiddleware'
CanonicalURLMiddleware: '%$SilverStripe\Control\Middleware\CanonicalURLMiddleware'
SilverStripe\Control\Middleware\AllowedHostsMiddleware:
properties:
AllowedHosts: '`SS_ALLOWED_HOSTS`'
Expand All @@ -37,3 +38,12 @@ After:
SilverStripe\Core\Injector\Injector:
# Note: If Director config changes, take note it will affect this config too
SilverStripe\Core\Startup\ErrorDirector: '%$SilverStripe\Control\Director'
---
Name: canonicalurls
---
SilverStripe\Core\Injector\Injector:
SilverStripe\Control\Middleware\CanonicalURLMiddleware:
properties:
ForceSSL: false
ForceWWW: false

121 changes: 35 additions & 86 deletions src/Control/Director.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace SilverStripe\Control;

use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Control\Middleware\CanonicalURLMiddleware;
use SilverStripe\Control\Middleware\HTTPMiddlewareAware;
use SilverStripe\Core\Config\Configurable;
use SilverStripe\Core\Environment;
Expand Down Expand Up @@ -242,7 +243,7 @@ public static function mockRequest(

// If a port is mentioned in the absolute URL, be sure to add that into the HTTP host
$newVars['_SERVER']['HTTP_HOST'] = isset($bits['port'])
? $bits['host'].':'.$bits['port']
? $bits['host'] . ':' . $bits['port']
: $bits['host'];
}

Expand Down Expand Up @@ -595,53 +596,34 @@ public static function baseFolder()
* Turns an absolute URL or folder into one that's relative to the root of the site. This is useful
* when turning a URL into a filesystem reference, or vice versa.
*
* @param string $url Accepts both a URL or a filesystem path.
* Note: You should check {@link Director::is_site_url()} if making an untrusted url relative prior
* to calling this function.
*
* @param string $url Accepts both a URL or a filesystem path.
* @return string
*/
public static function makeRelative($url)
{
// Allow for the accidental inclusion whitespace and // in the URL
$url = trim(preg_replace('#([^:])//#', '\\1/', $url));

$base1 = self::absoluteBaseURL();
$baseDomain = substr($base1, strlen(self::protocol()));

// Only bother comparing the URL to the absolute version if $url looks like a URL.
if (preg_match('/^https?[^:]*:\/\//', $url, $matches)) {
$urlProtocol = $matches[0];
$urlWithoutProtocol = substr($url, strlen($urlProtocol));

// If we are already looking at baseURL, return '' (substr will return false)
if ($url == $base1) {
return '';
} elseif (substr($url, 0, strlen($base1)) == $base1) {
return substr($url, strlen($base1));
} elseif (substr($base1, -1) == "/" && $url == substr($base1, 0, -1)) {
// Convert http://www.mydomain.com/mysitedir to ''
return "";
}
$url = preg_replace('#([^:])//#', '\\1/', trim($url));

if (substr($urlWithoutProtocol, 0, strlen($baseDomain)) == $baseDomain) {
return substr($urlWithoutProtocol, strlen($baseDomain));
}
// If using a real url, remove protocol / hostname / auth / port
if (preg_match('#^(?<protocol>https?:)?//(?<hostpart>[^/]*)(?<url>(/.*)?)$#i', $url, $matches)) {
$url = $matches['url'];
}

// test for base folder, e.g. /var/www
$base2 = self::baseFolder();
if (substr($url, 0, strlen($base2)) == $base2) {
return substr($url, strlen($base2));
// Empty case
if (trim($url, '\\/') === '') {
return '';
}

// Test for relative base url, e.g. mywebsite/ if the full URL is http://localhost/mywebsite/
$base3 = self::baseURL();
if (substr($url, 0, strlen($base3)) == $base3) {
return substr($url, strlen($base3));
}

// Test for relative base url, e.g mywebsite/ if the full url is localhost/myswebsite
if (substr($url, 0, strlen($baseDomain)) == $baseDomain) {
return substr($url, strlen($baseDomain));
// Remove base folder or url
foreach ([self::baseFolder(), self::baseURL()] as $base) {
// Ensure single / doesn't break comparison (unless it would make base empty)
$base = rtrim($base, '\\/') ?: $base;
if (stripos($url, $base) === 0) {
return ltrim(substr($url, strlen($base)), '\\/');
}
}

// Nothing matched, fall back to returning the original URL
Expand Down Expand Up @@ -697,10 +679,10 @@ public static function is_absolute_url($url)
{
// Strip off the query and fragment parts of the URL before checking
if (($queryPosition = strpos($url, '?')) !== false) {
$url = substr($url, 0, $queryPosition-1);
$url = substr($url, 0, $queryPosition - 1);
}
if (($hashPosition = strpos($url, '#')) !== false) {
$url = substr($url, 0, $hashPosition-1);
$url = substr($url, 0, $hashPosition - 1);
}
$colonPosition = strpos($url, ':');
$slashPosition = strpos($url, '/');
Expand Down Expand Up @@ -809,7 +791,7 @@ public static function absoluteBaseURLWithAuth(HTTPRequest $request = null)
$login = "$_SERVER[PHP_AUTH_USER]:$_SERVER[PHP_AUTH_PW]@";
}

return Director::protocol($request) . $login . static::host($request) . Director::baseURL();
return Director::protocol($request) . $login . static::host($request) . Director::baseURL();
}

/**
Expand Down Expand Up @@ -855,62 +837,29 @@ protected static function force_redirect($destURL)
*
* @param array $patterns Array of regex patterns to match URLs that should be HTTPS.
* @param string $secureDomain Secure domain to redirect to. Defaults to the current domain.
* @return bool true if already on SSL, false if doesn't match patterns (or cannot redirect)
* @throws HTTPResponse_Exception Throws exception with redirect, if successful
* @param HTTPRequest|null $request Request object to check
*/
public static function forceSSL($patterns = null, $secureDomain = null)
public static function forceSSL($patterns = null, $secureDomain = null, HTTPRequest $request = null)
{
// Already on SSL
if (static::is_https()) {
return true;
}

// Can't redirect without a url
if (!isset($_SERVER['REQUEST_URI'])) {
return false;
}

$handler = CanonicalURLMiddleware::singleton()->setForceSSL(true);
if ($patterns) {
$matched = false;
$relativeURL = self::makeRelative(Director::absoluteURL($_SERVER['REQUEST_URI']));

// protect portions of the site based on the pattern
foreach ($patterns as $pattern) {
if (preg_match($pattern, $relativeURL)) {
$matched = true;
break;
}
}
if (!$matched) {
return false;
}
$handler->setForceSSLPatterns($patterns);
}

// if an domain is specified, redirect to that instead of the current domain
if (!$secureDomain) {
$secureDomain = static::host();
if ($secureDomain) {
$handler->setForceSSLDomain($secureDomain);
}
$url = 'https://' . $secureDomain . $_SERVER['REQUEST_URI'];

// Force redirect
self::force_redirect($url);
return true;
$handler->throwRedirectIfNeeded($request);
}

/**
* Force a redirect to a domain starting with "www."
*
* @param HTTPRequest $request
*/
public static function forceWWW()
public static function forceWWW(HTTPRequest $request = null)
{
if (!Director::isDev() && !Director::isTest() && strpos(static::host(), 'www') !== 0) {
$destURL = str_replace(
Director::protocol(),
Director::protocol() . 'www.',
Director::absoluteURL($_SERVER['REQUEST_URI'])
);

self::force_redirect($destURL);
}
$handler = CanonicalURLMiddleware::singleton()->setForceWWW(true);
$handler->throwRedirectIfNeeded($request);
}

/**
Expand Down Expand Up @@ -947,7 +896,7 @@ public static function is_cli()
* Can also be checked with {@link Director::isDev()}, {@link Director::isTest()}, and
* {@link Director::isLive()}.
*
* @return bool
* @return string
*/
public static function get_environment_type()
{
Expand Down
Loading

0 comments on commit cd55a03

Please sign in to comment.