diff --git a/.htaccess b/.htaccess
index a9ecde6a53358..61a6e70d03e7f 100644
--- a/.htaccess
+++ b/.htaccess
@@ -206,7 +206,7 @@
###########################################
## Deny access to root files to hide sensitive application information
- RedirectMatch 404 /\.git
+ RedirectMatch 403 /\.git
order allow,deny
@@ -277,10 +277,14 @@
deny from all
- order allow,deny
- deny from all
+ order allow,deny
+ deny from all
+# For 404s and 403s that aren't handled by the application, show plain 404 response
+ErrorDocument 404 /pub/errors/404.php
+ErrorDocument 403 /pub/errors/404.php
+
################################
## If running in cluster environment, uncomment this
## http://developer.yahoo.com/performance/rules.html#etags
diff --git a/.htaccess.sample b/.htaccess.sample
index a6818c1e6b89a..61a6e70d03e7f 100644
--- a/.htaccess.sample
+++ b/.htaccess.sample
@@ -1,11 +1,12 @@
############################################
-## Optional override of deployment mode. We recommend you use the
-## command bin/magento deploy:mode:set to switch modes instead
-# SetEnv MAGE_MODE default # or production or developer
+## overrides deployment configuration mode value
+## use command bin/magento deploy:mode:set to switch modes
+
+# SetEnv MAGE_MODE developer
############################################
-## Uncomment these lines for CGI mode.
-## Make sure to specify the correct cgi php binary file name
+## uncomment these lines for CGI mode
+## make sure to specify the correct cgi php binary file name
## it might be /cgi-bin/php-cgi
# Action php5-cgi /cgi-bin/php5-cgi
@@ -16,42 +17,42 @@
# Options -MultiViews
-## You might also need to add this line to php.ini
+## you might also need to add this line to php.ini
## cgi.fix_pathinfo = 1
-## If it still doesn't work, rename php.ini to php5.ini
+## if it still doesn't work, rename php.ini to php5.ini
############################################
-## This line is specific for 1and1 hosting
+## this line is specific for 1and1 hosting
#AddType x-mapp-php5 .php
#AddHandler x-mapp-php5 .php
############################################
-## Default index file
+## default index file
DirectoryIndex index.php
############################################
-## Adjust memory limit
+## adjust memory limit
php_value memory_limit 768M
php_value max_execution_time 18000
############################################
-## Disable automatic session start
+## disable automatic session start
## before autoload was initialized
php_flag session.auto_start off
############################################
-## Enable resulting html compression
+## enable resulting html compression
#php_flag zlib.output_compression on
###########################################
-## Disable user agent verification to not break multiple image upload
+## disable user agent verification to not break multiple image upload
php_flag suhosin.session.cryptua off
@@ -60,24 +61,24 @@
############################################
-## Adjust memory limit
+## adjust memory limit
php_value memory_limit 768M
php_value max_execution_time 18000
############################################
-## Disable automatic session start
+## disable automatic session start
## before autoload was initialized
php_flag session.auto_start off
############################################
-## Enable resulting html compression
+## enable resulting html compression
#php_flag zlib.output_compression on
###########################################
-## Disable user agent verification to not break multiple image upload
+## disable user agent verification to not break multiple image upload
php_flag suhosin.session.cryptua off
@@ -85,7 +86,7 @@
###########################################
-## Disable POST processing to not break multiple image upload
+## disable POST processing to not break multiple image upload
SecFilterEngine Off
SecFilterScanPOST Off
@@ -94,7 +95,7 @@
############################################
-## Enable apache served files compression
+## enable apache served files compression
## http://developer.yahoo.com/performance/rules.html#gzip
# Insert filter on all content
@@ -122,14 +123,14 @@
############################################
-## Make HTTPS env vars available for CGI mode
+## make HTTPS env vars available for CGI mode
SSLOptions StdEnvVars
############################################
-## Workaround for Apache 2.4.6 CentOS build when working via ProxyPassMatch with HHVM (or any other)
+## workaround for Apache 2.4.6 CentOS build when working via ProxyPassMatch with HHVM (or any other)
## Please, set it on virtual host configuration level
## SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1
@@ -138,19 +139,19 @@
############################################
-## Enable rewrites
+## enable rewrites
Options +FollowSymLinks
RewriteEngine on
############################################
-## You can put here your magento root folder
+## you can put here your magento root folder
## path relative to web root
#RewriteBase /magento/
############################################
-## Workaround for HTTP authorization
+## workaround for HTTP authorization
## in CGI environment
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
@@ -162,21 +163,21 @@
RewriteRule .* - [L,R=405]
############################################
-## Redirect for mobile user agents
+## redirect for mobile user agents
#RewriteCond %{REQUEST_URI} !^/mobiledirectoryhere/.*$
#RewriteCond %{HTTP_USER_AGENT} "android|blackberry|ipad|iphone|ipod|iemobile|opera mobile|palmos|webos|googlebot-mobile" [NC]
#RewriteRule ^(.*)$ /mobiledirectoryhere/ [L,R=302]
############################################
-## Never rewrite for existing files, directories and links
+## never rewrite for existing files, directories and links
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-l
############################################
-## Rewrite everything else to index.php
+## rewrite everything else to index.php
RewriteRule .* index.php [L]
@@ -205,7 +206,7 @@
###########################################
## Deny access to root files to hide sensitive application information
- RedirectMatch 404 /\.git
+ RedirectMatch 403 /\.git
order allow,deny
@@ -280,6 +281,10 @@
deny from all
+# For 404s and 403s that aren't handled by the application, show plain 404 response
+ErrorDocument 404 /pub/errors/404.php
+ErrorDocument 403 /pub/errors/404.php
+
################################
## If running in cluster environment, uncomment this
## http://developer.yahoo.com/performance/rules.html#etags
diff --git a/app/code/Magento/PageCache/Model/Config.php b/app/code/Magento/PageCache/Model/Config.php
index 729da5f73f45c..222d9d57e467a 100644
--- a/app/code/Magento/PageCache/Model/Config.php
+++ b/app/code/Magento/PageCache/Model/Config.php
@@ -147,7 +147,13 @@ protected function _getReplacements()
\Magento\Store\Model\ScopeInterface::SCOPE_STORE
),
'/* {{ ips }} */' => $this->_getAccessList(),
- '/* {{ design_exceptions_code }} */' => $this->_getDesignExceptions()
+ '/* {{ design_exceptions_code }} */' => $this->_getDesignExceptions(),
+ // http headers get transformed by php `X-Forwarded-Proto: https` becomes $SERVER['HTTP_X_FORWARDED_PROTO'] = 'https'
+ // Apache and Nginx drop all headers with underlines by default.
+ '/* {{ ssl_offloaded_header }} */' => str_replace('_', '-', $this->_scopeConfig->getValue(
+ \Magento\Framework\HTTP\PhpEnvironment\Request::XML_PATH_OFFLOADER_HEADER,
+ \Magento\Store\Model\ScopeInterface::SCOPE_STORE))
+
];
}
diff --git a/app/code/Magento/PageCache/Observer/ProcessLayoutRenderElement.php b/app/code/Magento/PageCache/Observer/ProcessLayoutRenderElement.php
index f811e0386b56c..aa96c1060207e 100644
--- a/app/code/Magento/PageCache/Observer/ProcessLayoutRenderElement.php
+++ b/app/code/Magento/PageCache/Observer/ProcessLayoutRenderElement.php
@@ -59,6 +59,8 @@ protected function _wrapEsi(
'handles' => json_encode($layout->getUpdate()->getHandles())
]
);
+ // Varnish does not support ESI over HTTPS must change to HTTP
+ $url = substr($url, 0, 5) === 'https' ? 'http' . substr($url, 5) : $url;
return sprintf('', $url);
}
diff --git a/app/code/Magento/PageCache/etc/varnish3.vcl b/app/code/Magento/PageCache/etc/varnish3.vcl
index 70a56aa46f052..763e462756d94 100644
--- a/app/code/Magento/PageCache/etc/varnish3.vcl
+++ b/app/code/Magento/PageCache/etc/varnish3.vcl
@@ -1,5 +1,7 @@
import std;
# The minimal Varnish version is 3.0.5
+# For SSL offloading, pass the following header in your proxy server or load balancer: '/* {{ ssl_offloaded_header }} */: https'
+
backend default {
.host = "/* {{ host }} */";
@@ -61,6 +63,7 @@ sub vcl_recv {
# static files are always cacheable. remove SSL flag and cookie
if (req.url ~ "^/(pub/)?(media|static)/.*\.(ico|css|js|jpg|jpeg|png|gif|tiff|bmp|gz|tgz|bz2|tbz|mp3|ogg|svg|swf|woff|woff2|eot|ttf|otf)$") {
unset req.http.Https;
+ unset req.http./* {{ ssl_offloaded_header }} */;
unset req.http.Cookie;
}
@@ -73,6 +76,10 @@ sub vcl_hash {
if (req.http.cookie ~ "X-Magento-Vary=") {
hash_data(regsub(req.http.cookie, "^.*?X-Magento-Vary=([^;]+);*.*$", "\1"));
}
+
+ if (req.http./* {{ ssl_offloaded_header }} */) {
+ hash_data(req.http./* {{ ssl_offloaded_header }} */);
+ }
/* {{ design_exceptions_code }} */
}
diff --git a/app/code/Magento/PageCache/etc/varnish4.vcl b/app/code/Magento/PageCache/etc/varnish4.vcl
index d9931c8eff35a..49a8fe1585519 100644
--- a/app/code/Magento/PageCache/etc/varnish4.vcl
+++ b/app/code/Magento/PageCache/etc/varnish4.vcl
@@ -2,6 +2,7 @@ vcl 4.0;
import std;
# The minimal Varnish version is 4.0
+# For SSL offloading, pass the following header in your proxy server or load balancer: '/* {{ ssl_offloaded_header }} */: https'
backend default {
.host = "/* {{ host }} */";
@@ -74,6 +75,7 @@ sub vcl_recv {
# static files are always cacheable. remove SSL flag and cookie
if (req.url ~ "^/(pub/)?(media|static)/.*\.(ico|css|js|jpg|jpeg|png|gif|tiff|bmp|mp3|ogg|svg|swf|woff|woff2|eot|ttf|otf)$") {
unset req.http.Https;
+ unset req.http./* {{ ssl_offloaded_header }} */;
unset req.http.Cookie;
}
@@ -93,8 +95,8 @@ sub vcl_hash {
}
# To make sure http users don't see ssl warning
- if (req.http.X-Forwarded-Proto) {
- hash_data(req.http.X-Forwarded-Proto);
+ if (req.http./* {{ ssl_offloaded_header }} */) {
+ hash_data(req.http./* {{ ssl_offloaded_header }} */);
}
/* {{ design_exceptions_code }} */
}
diff --git a/app/code/Magento/Store/etc/config.xml b/app/code/Magento/Store/etc/config.xml
index cf3abe126ea08..6bcc78be7baa4 100644
--- a/app/code/Magento/Store/etc/config.xml
+++ b/app/code/Magento/Store/etc/config.xml
@@ -76,7 +76,7 @@
{{secure_base_url}}
0
0
- SSL_OFFLOADED
+ X-Forwarded-Proto
0
diff --git a/app/code/Magento/Webapi/Test/Unit/Controller/SoapTest.php b/app/code/Magento/Webapi/Test/Unit/Controller/SoapTest.php
index 32b2bfdcf352a..8698cb47dcc19 100644
--- a/app/code/Magento/Webapi/Test/Unit/Controller/SoapTest.php
+++ b/app/code/Magento/Webapi/Test/Unit/Controller/SoapTest.php
@@ -49,12 +49,16 @@ class SoapTest extends \PHPUnit_Framework_TestCase
*/
protected $_appStateMock;
+
+ protected $_appconfig;
/**
* Set up Controller object.
*/
protected function setUp()
{
parent::setUp();
+
+ $objectManagerHelper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
$this->_soapServerMock = $this->getMockBuilder('Magento\Webapi\Model\Soap\Server')
->disableOriginalConstructor()
@@ -95,6 +99,15 @@ protected function setUp()
->method('getHeaders')
->will($this->returnValue(new \Zend\Http\Headers()));
+ $appconfig = $this->getMock(\Magento\Framework\App\Config::class, [], [], '' , false);
+ $objectManagerHelper->setBackwardCompatibleProperty(
+ $this->_requestMock,
+ 'appConfig',
+ $appconfig
+ );
+
+
+
$this->_soapServerMock->expects($this->any())->method('setWSDL')->will($this->returnSelf());
$this->_soapServerMock->expects($this->any())->method('setEncoding')->will($this->returnSelf());
$this->_soapServerMock->expects($this->any())->method('setReturnResponse')->will($this->returnSelf());
diff --git a/dev/tests/integration/testsuite/Magento/Store/Model/StoreTest.php b/dev/tests/integration/testsuite/Magento/Store/Model/StoreTest.php
index 8be53da3b14d2..1b34a887f2052 100644
--- a/dev/tests/integration/testsuite/Magento/Store/Model/StoreTest.php
+++ b/dev/tests/integration/testsuite/Magento/Store/Model/StoreTest.php
@@ -372,7 +372,7 @@ public function isUseStoreInUrlDataProvider()
*
* @param bool $expected
* @param array $serverValues
- * @magentoConfigFixture current_store web/secure/offloader_header SSL_OFFLOADED
+ * @magentoConfigFixture current_store web/secure/offloader_header X_FORWARDED_PROTO
* @magentoConfigFixture current_store web/secure/base_url https://example.com:80
*/
public function testIsCurrentlySecure($expected, $serverValues)
@@ -391,8 +391,8 @@ public function isCurrentlySecureDataProvider()
{
return [
[true, ['HTTPS' => 'on']],
- [true, ['SSL_OFFLOADED' => 'https']],
- [true, ['HTTP_SSL_OFFLOADED' => 'https']],
+ [true, ['X_FORWARDED_PROTO' => 'https']],
+ [true, ['HTTP_X_FORWARDED_PROTO' => 'https']],
[true, ['HTTPS' => 'on', 'SERVER_PORT' => 80]],
[false, ['SERVER_PORT' => 80]],
[false, []],
diff --git a/lib/internal/Magento/Framework/App/Bootstrap.php b/lib/internal/Magento/Framework/App/Bootstrap.php
index 6658b099c1204..2420e5ac72362 100644
--- a/lib/internal/Magento/Framework/App/Bootstrap.php
+++ b/lib/internal/Magento/Framework/App/Bootstrap.php
@@ -404,15 +404,18 @@ public function getErrorCode()
*/
public function isDeveloperMode()
{
- if (isset($this->server[State::PARAM_MODE]) && $this->server[State::PARAM_MODE] == State::MODE_DEVELOPER) {
- return true;
- }
- /** @var \Magento\Framework\App\DeploymentConfig $deploymentConfig */
- $deploymentConfig = $this->getObjectManager()->get('Magento\Framework\App\DeploymentConfig');
- if ($deploymentConfig->get(State::PARAM_MODE) == State::MODE_DEVELOPER) {
- return true;
+ $mode = 'default';
+ if (isset($this->server[State::PARAM_MODE])) {
+ $mode = $this->server[State::PARAM_MODE];
+ } else {
+ $deploymentConfig = $this->getObjectManager()->get(DeploymentConfig::class);
+ $configMode = $deploymentConfig->get(State::PARAM_MODE);
+ if ($configMode) {
+ $mode = $configMode;
+ }
}
- return false;
+
+ return $mode == State::MODE_DEVELOPER;
}
/**
diff --git a/lib/internal/Magento/Framework/App/Config/ScopePool.php b/lib/internal/Magento/Framework/App/Config/ScopePool.php
index 172d1117d2e8f..d366349722f0f 100644
--- a/lib/internal/Magento/Framework/App/Config/ScopePool.php
+++ b/lib/internal/Magento/Framework/App/Config/ScopePool.php
@@ -91,8 +91,14 @@ private function getRequest()
public function getScope($scopeType, $scopeCode = null)
{
$scopeCode = $this->_getScopeCode($scopeType, $scopeCode);
- $baseUrl = $this->getRequest()->getDistroBaseUrl();
- $code = $scopeType . '|' . $scopeCode . '|' . $baseUrl;
+
+ // Key by url to support dynamic {{base_url}} and port assignments
+ $host = $this->getRequest()->getHttpHost();
+ $port = $this->getRequest()->getServer('SERVER_PORT');
+ $path = $this->getRequest()->getBasePath();
+ $urlInfo = $host . $port . trim($path, '/');
+ $code = $scopeType . '|' . $scopeCode . '|' . $urlInfo;
+
if (!isset($this->_scopes[$code])) {
$cacheKey = $this->_cacheId . '|' . $code;
$data = $this->_cache->load($cacheKey);
diff --git a/lib/internal/Magento/Framework/App/Request/Http.php b/lib/internal/Magento/Framework/App/Request/Http.php
index 2bb5380be937a..434a2e3ebae16 100644
--- a/lib/internal/Magento/Framework/App/Request/Http.php
+++ b/lib/internal/Magento/Framework/App/Request/Http.php
@@ -5,7 +5,6 @@
*/
namespace Magento\Framework\App\Request;
-use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\RequestInterface;
use Magento\Framework\App\RequestSafetyInterface;
use Magento\Framework\App\Route\ConfigInterface\Proxy as ConfigInterface;
@@ -318,16 +317,14 @@ public function getDistroBaseUrl()
{
$headerHttpHost = $this->getServer('HTTP_HOST');
$headerHttpHost = $this->converter->cleanString($headerHttpHost);
- $headerServerPort = $this->getServer('SERVER_PORT');
$headerScriptName = $this->getServer('SCRIPT_NAME');
- $headerHttps = $this->getServer('HTTPS');
if (isset($headerScriptName) && isset($headerHttpHost)) {
- $secure = !empty($headerHttps)
- && $headerHttps != 'off'
- || isset($headerServerPort)
- && $headerServerPort == '443';
- $scheme = ($secure ? 'https' : 'http') . '://';
+ if ($secure = $this->isSecure()) {
+ $scheme = 'https://';
+ } else {
+ $scheme = 'http://';
+ }
$hostArr = explode(':', $headerHttpHost);
$host = $hostArr[0];
@@ -403,29 +400,7 @@ public function __sleep()
return [];
}
- /**
- * {@inheritdoc}
- *
- * @return bool
- */
- public function isSecure()
- {
- if ($this->immediateRequestSecure()) {
- return true;
- }
- /* TODO: Untangle Config dependence on Scope, so that this class can be instantiated even if app is not
- installed MAGETWO-31756 */
- // Check if a proxy sent a header indicating an initial secure request
- $config = $this->objectManager->get('Magento\Framework\App\Config');
- $offLoaderHeader = trim(
- (string)$config->getValue(
- self::XML_PATH_OFFLOADER_HEADER,
- ScopeConfigInterface::SCOPE_TYPE_DEFAULT
- )
- );
-
- return $this->initialRequestSecure($offLoaderHeader);
- }
+
/**
* {@inheritdoc}
@@ -442,28 +417,5 @@ public function isSafeMethod()
return $this->isSafeMethod;
}
- /**
- * Checks if the immediate request is delivered over HTTPS
- *
- * @return bool
- */
- protected function immediateRequestSecure()
- {
- $https = $this->getServer('HTTPS');
- return !empty($https) && ($https != 'off');
- }
-
- /**
- * In case there is a proxy server, checks if the initial request to the proxy was delivered over HTTPS
- *
- * @param string $offLoaderHeader
- * @return bool
- */
- protected function initialRequestSecure($offLoaderHeader)
- {
- $header = $this->getServer($offLoaderHeader);
- $httpHeader = $this->getServer('HTTP_' . $offLoaderHeader);
- return !empty($offLoaderHeader)
- && (isset($header) && ($header === 'https') || isset($httpHeader) && ($httpHeader === 'https'));
- }
+
}
diff --git a/lib/internal/Magento/Framework/App/StaticResource.php b/lib/internal/Magento/Framework/App/StaticResource.php
index d591debb68f72..4367e8905e8c2 100644
--- a/lib/internal/Magento/Framework/App/StaticResource.php
+++ b/lib/internal/Magento/Framework/App/StaticResource.php
@@ -5,7 +5,9 @@
*/
namespace Magento\Framework\App;
+use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\ObjectManager\ConfigLoaderInterface;
+use Magento\Framework\Filesystem;
/**
* Entry point for retrieving static resources like JS, CSS, images by requested public path
@@ -14,46 +16,33 @@
*/
class StaticResource implements \Magento\Framework\AppInterface
{
- /**
- * @var State
- */
+ /** @var State */
private $state;
- /**
- * @var \Magento\Framework\App\Response\FileInterface
- */
+ /** @var \Magento\Framework\App\Response\FileInterface */
private $response;
- /**
- * @var Request\Http
- */
+ /** @var Request\Http */
private $request;
- /**
- * @var View\Asset\Publisher
- */
+ /** @var View\Asset\Publisher */
private $publisher;
- /**
- * @var \Magento\Framework\View\Asset\Repository
- */
+ /** @var \Magento\Framework\View\Asset\Repository */
private $assetRepo;
- /**
- * @var \Magento\Framework\Module\ModuleList
- */
+ /** @var \Magento\Framework\Module\ModuleList */
private $moduleList;
- /**
- * @var \Magento\Framework\ObjectManagerInterface
- */
+ /** @var \Magento\Framework\ObjectManagerInterface */
private $objectManager;
- /**
- * @var ConfigLoaderInterface
- */
+ /** @var ConfigLoaderInterface */
private $configLoader;
+ /** @var Filesystem */
+ private $filesystem;
+
/**
* @param State $state
* @param Response\FileInterface $response
@@ -116,12 +105,14 @@ public function launch()
*/
public function catchException(Bootstrap $bootstrap, \Exception $exception)
{
- $this->response->setHttpResponseCode(404);
- $this->response->setHeader('Content-Type', 'text/plain');
if ($bootstrap->isDeveloperMode()) {
+ $this->response->setHttpResponseCode(404);
+ $this->response->setHeader('Content-Type', 'text/plain');
$this->response->setBody($exception->getMessage() . "\n" . $exception->getTraceAsString());
+ $this->response->sendResponse();
+ } else {
+ require $this->getFilesystem()->getDirectoryRead(DirectoryList::PUB)->getAbsolutePath('errors/404.php');
}
- $this->response->sendResponse();
return true;
}
@@ -156,4 +147,18 @@ protected function parsePath($path)
$result['file'] = $parts[5];
return $result;
}
+
+ /**
+ * Lazyload filesystem driver
+ *
+ * @deprecated
+ * @return Filesystem
+ */
+ private function getFilesystem()
+ {
+ if (!$this->filesystem) {
+ $this->filesystem = $this->objectManager->get(Filesystem::class);
+ }
+ return $this->filesystem;
+ }
}
diff --git a/lib/internal/Magento/Framework/App/Test/Unit/BootstrapTest.php b/lib/internal/Magento/Framework/App/Test/Unit/BootstrapTest.php
index e4b5de3f74ee8..8929407ce48b8 100644
--- a/lib/internal/Magento/Framework/App/Test/Unit/BootstrapTest.php
+++ b/lib/internal/Magento/Framework/App/Test/Unit/BootstrapTest.php
@@ -162,26 +162,35 @@ public function testGetObjectManager()
$this->assertSame($this->objectManager, $bootstrap->getObjectManager());
}
- public function testIsDeveloperMode()
+ /**
+ * @param $modeFromEnvironment
+ * @param $modeFromDeployment
+ * @param $isDeveloper
+ *
+ * @dataProvider testIsDeveloperModeDataProvider
+ */
+ public function testIsDeveloperMode($modeFromEnvironment, $modeFromDeployment, $isDeveloper)
{
- $bootstrap = self::createBootstrap();
- $this->assertFalse($bootstrap->isDeveloperMode());
- $testParams = [State::PARAM_MODE => State::MODE_DEVELOPER];
+ $testParams = [];
+ if ($modeFromEnvironment) {
+ $testParams[State::PARAM_MODE] = $modeFromEnvironment;
+ }
+ if ($modeFromDeployment) {
+ $this->deploymentConfig->method('get')->willReturn($modeFromDeployment);
+ }
$bootstrap = self::createBootstrap($testParams);
- $this->assertTrue($bootstrap->isDeveloperMode());
- $this->deploymentConfig->expects($this->any())->method('get')->willReturn(State::MODE_DEVELOPER);
- $bootstrap = self::createBootstrap();
- $this->assertTrue($bootstrap->isDeveloperMode());
+ $this->assertEquals($isDeveloper, $bootstrap->isDeveloperMode());
}
- public function testIsDeveloperModeСontradictoryValues()
+ public function testIsDeveloperModeDataProvider()
{
- $this->deploymentConfig->expects($this->any())->method('get')->willReturn(State::MODE_PRODUCTION);
- $bootstrap = self::createBootstrap();
- $this->assertFalse($bootstrap->isDeveloperMode());
- $testParams = [State::PARAM_MODE => State::MODE_DEVELOPER];
- $bootstrap = self::createBootstrap($testParams);
- $this->assertTrue($bootstrap->isDeveloperMode());
+ return [
+ [null, null, false],
+ [State::MODE_DEVELOPER, State::MODE_PRODUCTION, true],
+ [State::MODE_PRODUCTION, State::MODE_DEVELOPER, false],
+ [null, State::MODE_DEVELOPER, true],
+ [null, State::MODE_PRODUCTION, false]
+ ];
}
public function testRunNoErrors()
diff --git a/lib/internal/Magento/Framework/App/Test/Unit/Config/ScopePoolTest.php b/lib/internal/Magento/Framework/App/Test/Unit/Config/ScopePoolTest.php
index c0799b49eab76..ef95d05dd9c9d 100644
--- a/lib/internal/Magento/Framework/App/Test/Unit/Config/ScopePoolTest.php
+++ b/lib/internal/Magento/Framework/App/Test/Unit/Config/ScopePoolTest.php
@@ -58,7 +58,7 @@ protected function setUp()
->disableOriginalConstructor()
->setMethods(
[
- 'getDistroBaseUrl',
+ 'getBasePath',
'getModuleName',
'setModuleName',
'getActionName',
@@ -68,6 +68,8 @@ protected function setUp()
'setParams',
'getCookie',
'isSecure',
+ 'getServer',
+ 'getHttpHost'
]
)->getMock();
$reflection = new \ReflectionClass(get_class($this->_object));
@@ -75,7 +77,7 @@ protected function setUp()
$reflectionProperty->setAccessible(true);
$reflectionProperty->setValue($this->_object, $requestMock);
$requestMock->expects($this->any())
- ->method('getDistroBaseUrl')
+ ->method('getBasePath')
->willReturn('baseUrl');
}
diff --git a/lib/internal/Magento/Framework/App/Test/Unit/Request/HttpTest.php b/lib/internal/Magento/Framework/App/Test/Unit/Request/HttpTest.php
index 1dd2e50fd8334..fcb2046a14d65 100644
--- a/lib/internal/Magento/Framework/App/Test/Unit/Request/HttpTest.php
+++ b/lib/internal/Magento/Framework/App/Test/Unit/Request/HttpTest.php
@@ -32,15 +32,20 @@ class HttpTest extends \PHPUnit_Framework_TestCase
protected $_infoProcessorMock;
/**
- * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager | \PHPUnit_Framework_MockObject_MockObject
+ * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager | \PHPUnit_Framework_MockObject_MockObject
*/
- protected $objectManager;
+ protected $objectManagerMock;
/**
- * @var \Magento\Framework\Stdlib\StringUtils | \PHPUnit_Framework_MockObject_MockObject
+ * @var \Magento\Framework\Stdlib\StringUtils | \PHPUnit_Framework_MockObject_MockObject
*/
protected $converterMock;
+ /**
+ * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager
+ */
+ private $objectManager;
+
/**
* @var array
*/
@@ -58,7 +63,7 @@ protected function setUp()
);
$this->_infoProcessorMock = $this->getMock('Magento\Framework\App\Request\PathInfoProcessorInterface');
$this->_infoProcessorMock->expects($this->any())->method('process')->will($this->returnArgument(1));
- $this->objectManager = $this->getMock('Magento\Framework\ObjectManagerInterface');
+ $this->objectManagerMock = $this->getMock('Magento\Framework\ObjectManagerInterface');
$this->converterMock = $this->getMockBuilder('Magento\Framework\Stdlib\StringUtils')
->disableOriginalConstructor()
->setMethods(['cleanString'])
@@ -67,6 +72,8 @@ protected function setUp()
// Stash the $_SERVER array to protect it from modification in test
$this->serverArray = $_SERVER;
+
+ $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
}
public function tearDown()
@@ -77,19 +84,26 @@ public function tearDown()
/**
* @return \Magento\Framework\App\Request\Http
*/
- private function getModel($uri = null)
+ private function getModel($uri = null, $appConfigMock = true)
{
- $testFrameworkObjectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager ($this);
- return $testFrameworkObjectManager->getObject(
+
+ $model = $this->objectManager->getObject(
'Magento\Framework\App\Request\Http',
[
'routeConfig' => $this->_routerListMock,
'pathInfoProcessor' => $this->_infoProcessorMock,
- 'objectManager' => $this->objectManager,
+ 'objectManager' => $this->objectManagerMock,
'converter' => $this->converterMock,
'uri' => $uri,
]
);
+
+ if ($appConfigMock) {
+ $configMock = $this->getMock(\Magento\Framework\App\Config::class, [], [], '' , false);
+ $this->objectManager->setBackwardCompatibleProperty($model, 'appConfig', $configMock);
+ }
+
+ return $model;
}
public function testGetOriginalPathInfoWithTestUri()
@@ -329,7 +343,7 @@ public function serverVariablesProvider()
*/
public function testIsSecure($isSecure, $serverHttps, $headerOffloadKey, $headerOffloadValue, $configCall)
{
- $this->_model = $this->getModel();
+ $this->_model = $this->getModel(null, false);
$configOffloadHeader = 'Header-From-Proxy';
$configMock = $this->getMockBuilder('Magento\Framework\App\Config')
->disableOriginalConstructor()
@@ -339,10 +353,9 @@ public function testIsSecure($isSecure, $serverHttps, $headerOffloadKey, $header
->method('getValue')
->with(\Magento\Framework\App\Request\Http::XML_PATH_OFFLOADER_HEADER, ScopeConfigInterface::SCOPE_TYPE_DEFAULT)
->willReturn($configOffloadHeader);
- $this->objectManager->expects($this->exactly($configCall))
- ->method('get')
- ->with('Magento\Framework\App\Config')
- ->will($this->returnValue($configMock));
+
+ $this->objectManager->setBackwardCompatibleProperty($this->_model, 'appConfig', $configMock);
+ $this->objectManager->setBackwardCompatibleProperty($this->_model, 'sslOffloadHeader', null);
$this->_model->getServer()->set($headerOffloadKey, $headerOffloadValue);
$this->_model->getServer()->set('HTTPS', $serverHttps);
@@ -409,18 +422,18 @@ public function isSecureDataProvider()
* ]
*/
return [
- 'Test 1' => [true, 'on', 'Header-From-Proxy', 'https', 0],
- 'Test 2' => [true, 'off', 'Header-From-Proxy', 'https', 1],
- 'Test 3' => [true, 'any-string', 'Header-From-Proxy', 'https', 0],
- 'Test 4' => [true, 'on', 'Header-From-Proxy', 'http', 0],
- 'Test 5' => [false, 'off', 'Header-From-Proxy', 'http', 1],
- 'Test 6' => [true, 'any-string', 'Header-From-Proxy', 'http', 0],
- 'Test 7' => [true, 'on', 'Header-From-Proxy', 'any-string', 0],
- 'Test 8' => [false, 'off', 'Header-From-Proxy', 'any-string', 1],
- 'Test 9' => [true, 'any-string', 'Header-From-Proxy', 'any-string', 0],
- 'blank HTTPS with proxy set https' => [true, '', 'Header-From-Proxy', 'https', 1],
- 'blank HTTPS with proxy set http' => [false, '', 'Header-From-Proxy', 'http', 1],
- 'HTTPS off with HTTP_ prefixed proxy set to https' => [true, 'off', 'HTTP_Header-From-Proxy', 'https', 1],
+ 'Test 1' => [true, 'on', 'HEADER_FROM_PROXY', 'https', 0],
+ 'Test 2' => [true, 'off', 'HEADER_FROM_PROXY', 'https', 1],
+ 'Test 3' => [true, 'any-string', 'HEADER_FROM_PROXY', 'https', 0],
+ 'Test 4' => [true, 'on', 'HEADER_FROM_PROXY', 'http', 0],
+ 'Test 5' => [false, 'off', 'HEADER_FROM_PROXY', 'http', 1],
+ 'Test 6' => [true, 'any-string', 'HEADER_FROM_PROXY', 'http', 0],
+ 'Test 7' => [true, 'on', 'HEADER_FROM_PROXY', 'any-string', 0],
+ 'Test 8' => [false, 'off', 'HEADER_FROM_PROXY', 'any-string', 1],
+ 'Test 9' => [true, 'any-string', 'HEADER_FROM_PROXY', 'any-string', 0],
+ 'blank HTTPS with proxy set https' => [true, '', 'HEADER_FROM_PROXY', 'https', 1],
+ 'blank HTTPS with proxy set http' => [false, '', 'HEADER_FROM_PROXY', 'http', 1],
+ 'HTTPS off with HTTP_ prefixed proxy set to https' => [true, 'off', 'HTTP_HEADER_FROM_PROXY', 'https', 1],
];
}
}
diff --git a/lib/internal/Magento/Framework/App/Test/Unit/StaticResourceTest.php b/lib/internal/Magento/Framework/App/Test/Unit/StaticResourceTest.php
index 78d5cc7f5acc9..32e028347a61d 100644
--- a/lib/internal/Magento/Framework/App/Test/Unit/StaticResourceTest.php
+++ b/lib/internal/Magento/Framework/App/Test/Unit/StaticResourceTest.php
@@ -8,6 +8,9 @@
namespace Magento\Framework\App\Test\Unit;
+use Magento\Framework\App\Bootstrap;
+use Magento\Framework\Filesystem;
+
class StaticResourceTest extends \PHPUnit_Framework_TestCase
{
/**
@@ -188,17 +191,13 @@ public function testLaunchWrongPath()
$this->object->launch();
}
- public function testCatchException()
+ public function testCatchExceptionDeveloperMode()
{
- $bootstrap = $this->getMock('Magento\Framework\App\Bootstrap', [], [], '', false);
- $bootstrap->expects($this->at(0))->method('isDeveloperMode')->willReturn(false);
- $bootstrap->expects($this->at(1))->method('isDeveloperMode')->willReturn(true);
- $exception = new \Exception('message');
- $this->response->expects($this->exactly(2))->method('setHttpResponseCode')->with(404);
- $this->response->expects($this->exactly(2))->method('setHeader')->with('Content-Type', 'text/plain');
- $this->response->expects($this->exactly(2))->method('sendResponse');
- $this->response->expects($this->once())->method('setBody')->with($this->stringStartsWith('message'));
- $this->assertTrue($this->object->catchException($bootstrap, $exception));
+ $bootstrap = $this->getMockBuilder(Bootstrap::class)->disableOriginalConstructor()->getMock();
+ $bootstrap->expects($this->once())->method('isDeveloperMode')->willReturn(true);
+ $exception = new \Exception('Error: nothing works');
+ $this->response->expects($this->once())->method('setHttpResponseCode')->with(404);
+ $this->response->expects($this->once())->method('sendResponse');
$this->assertTrue($this->object->catchException($bootstrap, $exception));
}
}
diff --git a/lib/internal/Magento/Framework/HTTP/PhpEnvironment/Request.php b/lib/internal/Magento/Framework/HTTP/PhpEnvironment/Request.php
index 562346560c0b8..64f8dd4564e65 100644
--- a/lib/internal/Magento/Framework/HTTP/PhpEnvironment/Request.php
+++ b/lib/internal/Magento/Framework/HTTP/PhpEnvironment/Request.php
@@ -22,6 +22,9 @@ class Request extends \Zend\Http\PhpEnvironment\Request
const SCHEME_HTTPS = 'https';
/**#@-*/
+ // Configuration path for SSL Offload http header
+ const XML_PATH_OFFLOADER_HEADER = 'web/secure/offloader_header';
+
/**
* @var string
*/
@@ -85,6 +88,18 @@ class Request extends \Zend\Http\PhpEnvironment\Request
*/
protected $converter;
+ /**
+ * @var \Magento\Framework\App\Config
+ */
+ protected $appConfig;
+
+ /**
+ * Name of http header to check for ssl offloading default value is X-Forwarded-Proto
+ *
+ * @var string
+ */
+ protected $sslOffloadHeader;
+
/**
* @param CookieReaderInterface $cookieReader
* @param StringUtils $converter
@@ -364,7 +379,7 @@ public function clearParams()
*/
public function getScheme()
{
- return ($this->getServer('HTTPS') == 'on') ? self::SCHEME_HTTPS : self::SCHEME_HTTP;
+ return $this->isSecure() ? self::SCHEME_HTTPS : self::SCHEME_HTTP;
}
/**
@@ -396,7 +411,79 @@ public function isDispatched()
*/
public function isSecure()
{
- return ($this->getScheme() == self::SCHEME_HTTPS);
+ if ($this->immediateRequestSecure()) {
+ return true;
+ }
+
+ return $this->initialRequestSecure($this->SslOffloadHeader());
+ }
+
+ /***
+ * Get value of SSL offload http header from configuration - defaults to X-Forwarded-Proto
+ *
+ * @return string
+ */
+ private function SslOffloadHeader()
+ {
+ // Lets read from db only one time okay.
+ if ($this->sslOffloadHeader === null) {
+
+ // @todo: Untangle Config dependence on Scope, so that this class can be instantiated even if app is not
+ // installed MAGETWO-31756
+ // Check if a proxy sent a header indicating an initial secure request
+ $this->sslOffloadHeader = trim(
+ (string)$this->getAppConfig()->getValue(
+ self::XML_PATH_OFFLOADER_HEADER,
+ \Magento\Framework\App\Config\ScopeConfigInterface::SCOPE_TYPE_DEFAULT
+ )
+ );
+ }
+
+ return $this->sslOffloadHeader;
+ }
+
+ /**
+ * Create an instance of Magento\Framework\App\Config
+ *
+ * @return \Magento\Framework\App\Config
+ * @deprecated
+ */
+ private function getAppConfig()
+ {
+ if ($this->appConfig == null) {
+ $this->appConfig =
+ \Magento\Framework\App\ObjectManager::getInstance()->get(\Magento\Framework\App\Config::class);
+ }
+ return $this->appConfig;
+ }
+
+ /**
+ * Checks if the immediate request is delivered over HTTPS
+ *
+ * @return bool
+ */
+ protected function immediateRequestSecure()
+ {
+ $https = $this->getServer('HTTPS');
+ $headerServerPort = $this->getServer('SERVER_PORT');
+ return (!empty($https) && $https != 'off') || $headerServerPort == 443;
+ }
+
+ /**
+ * In case there is a proxy server, checks if the initial request to the proxy was delivered over HTTPS
+ *
+ * @param string $offLoaderHeader
+ * @return bool
+ */
+ protected function initialRequestSecure($offLoaderHeader)
+ {
+ // Transform http header to $_SERVER format ie X-Forwarded-Proto becomes $_SERVER['HTTP_X_FORWARDED_PROTO']
+ $offLoaderHeader = str_replace('-', '_', strtoupper($offLoaderHeader));
+ // Some webservers do not append HTTP_
+ $header = $this->getServer($offLoaderHeader);
+ // Apache appends HTTP_
+ $httpHeader = $this->getServer('HTTP_' . $offLoaderHeader);
+ return !empty($offLoaderHeader) && ($header === 'https' || $httpHeader === 'https');
}
/**
diff --git a/lib/internal/Magento/Framework/Test/Unit/UrlTest.php b/lib/internal/Magento/Framework/Test/Unit/UrlTest.php
index a1ecfb2ed4f88..95163d1ed8ae7 100644
--- a/lib/internal/Magento/Framework/Test/Unit/UrlTest.php
+++ b/lib/internal/Magento/Framework/Test/Unit/UrlTest.php
@@ -491,6 +491,7 @@ public function getRebuiltUrlDataProvider()
return [
'with port' => ['https://example.com:88/index.php/catalog/index/view?query=123#hash'],
'without port' => ['https://example.com/index.php/catalog/index/view?query=123#hash'],
+ 'http' => ['http://example.com/index.php/catalog/index/view?query=123#hash']
];
}
diff --git a/nginx.conf.sample b/nginx.conf.sample
index 3f9eaba55defa..95f03f4dd2145 100644
--- a/nginx.conf.sample
+++ b/nginx.conf.sample
@@ -30,6 +30,7 @@ root $MAGE_ROOT/pub;
index index.php;
autoindex off;
charset UTF-8;
+error_page 404 403 = /errors/404.php;
#add_header "X-UA-Compatible" "IE=Edge";
# PHP entry point for setup application
@@ -190,6 +191,7 @@ gzip_types
image/svg+xml;
gzip_vary on;
-location ~ \.php$ {
+# Banned locations (only reached if the earlier PHP entry point regexes don't match)
+location ~* (\.php$|\.htaccess$|\.git) {
deny all;
}
diff --git a/pub/.htaccess b/pub/.htaccess
index 6d70d0f19a838..926c012eef6a5 100644
--- a/pub/.htaccess
+++ b/pub/.htaccess
@@ -197,6 +197,10 @@
deny from all
+# For 404s and 403s that aren't handled by the application, show plain 404 response
+ErrorDocument 404 /errors/404.php
+ErrorDocument 403 /errors/404.php
+
############################################
## If running in cluster environment, uncomment this
## http://developer.yahoo.com/performance/rules.html#etags
diff --git a/pub/get.php b/pub/get.php
index 760f922adb140..3be707560b8c5 100644
--- a/pub/get.php
+++ b/pub/get.php
@@ -45,13 +45,13 @@
// Serve file if it's materialized
if ($mediaDirectory) {
if (!$isAllowed($relativePath, $allowedResources)) {
- header('HTTP/1.0 404 Not Found');
+ require_once 'errors/404.php';
exit;
}
$mediaAbsPath = $mediaDirectory . '/' . $relativePath;
if (is_readable($mediaAbsPath)) {
if (is_dir($mediaAbsPath)) {
- header('HTTP/1.0 404 Not Found');
+ require_once 'errors/404.php';
exit;
}
$transfer = new \Magento\Framework\File\Transfer\Adapter\Http(