diff --git a/core/control/Director.php b/core/control/Director.php
index 47b6cf66b..677730a4e 100755
--- a/core/control/Director.php
+++ b/core/control/Director.php
@@ -605,19 +605,55 @@ class Director {
}
/**
- * Force the site to run on SSL. To use, call from _config.php.
+ * Force the site to run on SSL.
*
- * For example:
+ * To use, call from _config.php. For example:
*
* if(Director::isLive()) Director::forceSSL();
*
+ *
+ * If you don't want your entire site to be on SSL, you can pass an array of PCRE regular expression
+ * patterns for matching relative URLs. For example:
+ *
+ * if(Director::isLive()) Director::forceSSL(array('/^admin/', '/^Security/.*'));
+ *
+ *
+ * Note that the session data will be lost when moving from HTTP to HTTPS.
+ * It is your responsibility to ensure that this won't cause usability problems.
+ *
+ * CAUTION: This does not respect the site environment mode. You should check this
+ * as per the above examples using Director::isLive() or Director::isTest() for example.
+ *
+ * @return boolean|string String of URL when unit tests running, boolean FALSE if patterns don't match request URI
*/
- static function forceSSL() {
- if((Director::protocol() != "https://") && !Director::isDev() && !Director::is_cli()) {
+ static function forceSSL($patterns = null) {
+ $matched = false;
+
+ if($patterns) {
+ // protect portions of the site based on the pattern
+ $relativeURL = self::makeRelative(Director::absoluteURL($_SERVER['REQUEST_URI']));
+ foreach($patterns as $pattern) {
+ if(preg_match($pattern, $relativeURL)) {
+ $matched = true;
+ break;
+ }
+ }
+ } else {
+ // protect the entire site
+ $matched = true;
+ }
+
+ if($matched && !isset($_SERVER['HTTPS'])) {
$destURL = str_replace('http:', 'https:', Director::absoluteURL($_SERVER['REQUEST_URI']));
- header("Location: $destURL", true, 301);
- die("
Please click here"); + header("Location: $destURL"); + if(SapphireTest::is_running_test()) { + return $destURL; + } else { + die("
Please click here"); + } + } else { + return false; } } diff --git a/tests/control/DirectorTest.php b/tests/control/DirectorTest.php index c643dc048..420c8eba6 100644 --- a/tests/control/DirectorTest.php +++ b/tests/control/DirectorTest.php @@ -5,7 +5,7 @@ * * @todo test Director::alternateBaseFolder() */ -class DirectorTest extends SapphireTest { +class DirectorTest extends FunctionalTest { function setUp() { parent::setUp(); @@ -189,7 +189,53 @@ class DirectorTest extends SapphireTest { ) ); } - + + function testForceSSLProtectsEntireSite() { + $originalURI = $_SERVER['REQUEST_URI']; + $_SERVER['REQUEST_URI'] = Director::baseURL() . 'admin'; + $output = Director::forceSSL(); + $this->assertEquals($output, 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']); + + $_SERVER['REQUEST_URI'] = $originalURI; + $_SERVER['REQUEST_URI'] = Director::baseURL() . 'some-url'; + $output = Director::forceSSL(); + $this->assertEquals($output, 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']); + + $_SERVER['REQUEST_URI'] = $originalURI; + } + + function testForceSSLOnTopLevelPagePattern() { + $originalURI = $_SERVER['REQUEST_URI']; + $_SERVER['REQUEST_URI'] = Director::baseURL() . 'admin'; + $output = Director::forceSSL(array('/^admin/')); + $this->assertEquals($output, 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']); + + $_SERVER['REQUEST_URI'] = $originalURI; + } + + function testForceSSLOnSubPagesPattern() { + $originalURI = $_SERVER['REQUEST_URI']; + $_SERVER['REQUEST_URI'] = Director::baseURL() . 'Security/login'; + $output = Director::forceSSL(array('/^Security/')); + $this->assertEquals($output, 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']); + + $_SERVER['REQUEST_URI'] = $originalURI; + } + + function testForceSSLWithPatternDoesNotMatchOtherPages() { + $originalURI = $_SERVER['REQUEST_URI']; + $_SERVER['REQUEST_URI'] = Director::baseURL() . 'normal-page'; + $output = Director::forceSSL(array('/^admin/')); + $this->assertFalse($output); + + $_SERVER['REQUEST_URI'] = $originalURI; + $_SERVER['REQUEST_URI'] = Director::baseURL() . 'just-another-page/sub-url'; + $output = Director::forceSSL(array('/^admin/', '/^Security/')); + $this->assertFalse($output); + + $_SERVER['REQUEST_URI'] = $originalURI; + } + } class DirectorTestRequest_Controller extends Controller implements TestOnly {