mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
BUG Fix flushing on live mode (#7241)
* BUG Fix flushing on live mode Fixes #7217 * Clarify injector service documentation
This commit is contained in:
parent
4dbc2a99e5
commit
0681567102
@ -3,6 +3,7 @@ Name: requestprocessors
|
||||
---
|
||||
SilverStripe\Core\Injector\Injector:
|
||||
SilverStripe\Control\Director:
|
||||
# Note: Don't add 'class' config here, as it will affect ErrorDirector as well
|
||||
properties:
|
||||
Middlewares:
|
||||
TrustedProxyMiddleware: '%$SilverStripe\Control\Middleware\TrustedProxyMiddleware'
|
||||
@ -16,3 +17,11 @@ SilverStripe\Core\Injector\Injector:
|
||||
SilverStripe\Control\TrustedProxyMiddleware:
|
||||
properties:
|
||||
TrustedProxyIPs: "`SS_TRUSTED_PROXY_IPS`"
|
||||
---
|
||||
Name: errorrequestprocessors
|
||||
After:
|
||||
- requestprocessors
|
||||
---
|
||||
SilverStripe\Core\Injector\Injector:
|
||||
# Note: If Director config changes, take note it will affect this config too
|
||||
SilverStripe\Core\Startup\ErrorDirector: '%$SilverStripe\Control\Director'
|
||||
|
@ -252,11 +252,31 @@ assign a reference to that service.
|
||||
:::yaml
|
||||
Injector:
|
||||
JSONServiceDefinition:
|
||||
class: JSONServiceImplementor
|
||||
properties:
|
||||
Serialiser: JSONSerialiser
|
||||
GZIPJSONProvider: %$JSONServiceDefinition
|
||||
|
||||
|
||||
`Injector::inst()->get('GZIPJSONProvider')` will then be an instance of `JSONServiceImplementor` with the injected
|
||||
properties.
|
||||
|
||||
It is important here to note that the 'class' property of the parent service will be inherited directly as well.
|
||||
If class is not specified, then the class will be inherited from the outer service name, not the inner service name.
|
||||
|
||||
For example with this config:
|
||||
|
||||
:::yaml
|
||||
Injector:
|
||||
Connector:
|
||||
properties:
|
||||
AsString: true
|
||||
ServiceConnector: %$Connector
|
||||
|
||||
|
||||
Both `Connector` and `ServiceConnector` will have the `AsString` property set to true, but the resulting
|
||||
instances will be classes which match their respective service names, due to the lack of a `class` specification.
|
||||
|
||||
## Testing with Injector
|
||||
|
||||
In situations where injector states must be temporarily overridden, it is possible to create nested Injector instances
|
||||
|
@ -442,6 +442,9 @@ Upgrade subclasses
|
||||
-class MyClass extends Object
|
||||
-{
|
||||
-}
|
||||
+use SilverStripe\Core\Extensible;
|
||||
+use SilverStripe\Core\Injector\Injectable;
|
||||
+use SilverStripe\Core\Config\Configurable;
|
||||
+class MyClass
|
||||
+{
|
||||
+ use Extensible;
|
||||
|
@ -6,9 +6,8 @@ use SilverStripe\Control\Director;
|
||||
use SilverStripe\Control\HTTPRequest;
|
||||
use SilverStripe\Control\HTTPResponse;
|
||||
use SilverStripe\Control\HTTPResponse_Exception;
|
||||
use SilverStripe\Core\Application;
|
||||
use SilverStripe\Control\Middleware\HTTPMiddleware;
|
||||
use SilverStripe\Security\Permission;
|
||||
use SilverStripe\Core\Application;
|
||||
use SilverStripe\Security\Security;
|
||||
|
||||
/**
|
||||
@ -87,9 +86,10 @@ class ErrorControlChainMiddleware implements HTTPMiddleware
|
||||
// Ensure session is started
|
||||
$request->getSession()->init($request);
|
||||
|
||||
// Next, check if we're in dev mode, or the database doesn't have any security data, or we are admin
|
||||
if (Director::isDev() || !Security::database_is_ready() || Permission::check('ADMIN')) {
|
||||
return $reloadToken->reloadWithToken();
|
||||
// Request with ErrorDirector
|
||||
$result = ErrorDirector::singleton()->handleRequestWithToken($request, $reloadToken, $this->getApplication()->getKernel());
|
||||
if ($result) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
// Fail and redirect the user to the login page
|
||||
|
47
src/Core/Startup/ErrorDirector.php
Normal file
47
src/Core/Startup/ErrorDirector.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Startup;
|
||||
|
||||
use SilverStripe\Control\Director;
|
||||
use SilverStripe\Control\HTTPRequest;
|
||||
use SilverStripe\Control\HTTPResponse;
|
||||
use SilverStripe\Core\Injector\Injector;
|
||||
use SilverStripe\Core\Kernel;
|
||||
use SilverStripe\Security\Permission;
|
||||
use SilverStripe\Security\Security;
|
||||
|
||||
/**
|
||||
* Specialised Director class used by ErrorControlChain to handle error and redirect conditions
|
||||
*
|
||||
* @internal This class is experimental API and may change without warning
|
||||
*/
|
||||
class ErrorDirector extends Director
|
||||
{
|
||||
/**
|
||||
* Redirect with token if allowed, or null if not allowed
|
||||
*
|
||||
* @param HTTPRequest $request
|
||||
* @param ParameterConfirmationToken $token
|
||||
* @param Kernel $kernel
|
||||
* @return null|HTTPResponse Redirection response, or null if not able to redirect
|
||||
*/
|
||||
public function handleRequestWithToken(HTTPRequest $request, ParameterConfirmationToken $token, Kernel $kernel)
|
||||
{
|
||||
Injector::inst()->registerService($request, HTTPRequest::class);
|
||||
|
||||
// Next, check if we're in dev mode, or the database doesn't have any security data, or we are admin
|
||||
$reload = function (HTTPRequest $request) use ($token, $kernel) {
|
||||
if ($kernel->getEnvironment() === Kernel::DEV || !Security::database_is_ready() || Permission::check('ADMIN')) {
|
||||
return $token->reloadWithToken();
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
try {
|
||||
return $this->callMiddleware($request, $reload);
|
||||
} finally {
|
||||
// Ensure registered request is un-registered
|
||||
Injector::inst()->unregisterNamedObject(HTTPRequest::class);
|
||||
}
|
||||
}
|
||||
}
|
@ -498,7 +498,7 @@ class DirectorTest extends SapphireTest
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \SilverStripe\Control\Director::extract_request_headers()
|
||||
* @covers \SilverStripe\Control\HTTPRequestBuilder::extractRequestHeaders()
|
||||
*/
|
||||
public function testExtractRequestHeaders()
|
||||
{
|
||||
|
77
tests/php/Core/Startup/ErrorControlChainMiddlewareTest.php
Normal file
77
tests/php/Core/Startup/ErrorControlChainMiddlewareTest.php
Normal file
@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Tests\Startup;
|
||||
|
||||
use SilverStripe\Control\Cookie;
|
||||
use SilverStripe\Control\HTTPApplication;
|
||||
use SilverStripe\Control\HTTPRequest;
|
||||
use SilverStripe\Control\HTTPResponse;
|
||||
use SilverStripe\Control\Session;
|
||||
use SilverStripe\Core\Kernel;
|
||||
use SilverStripe\Core\Startup\ErrorControlChainMiddleware;
|
||||
use SilverStripe\Core\Tests\Startup\ErrorControlChainMiddlewareTest\BlankKernel;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\Security\Security;
|
||||
|
||||
class ErrorControlChainMiddlewareTest extends SapphireTest
|
||||
{
|
||||
protected $usesDatabase = true;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
Security::force_database_is_ready(true);
|
||||
}
|
||||
|
||||
protected function tearDown()
|
||||
{
|
||||
Security::clear_database_is_ready();
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testLiveFlushAdmin()
|
||||
{
|
||||
// Mock admin
|
||||
$adminID = $this->logInWithPermission('ADMIN');
|
||||
$this->logOut();
|
||||
|
||||
// Mock app
|
||||
$app = new HTTPApplication(new BlankKernel(BASE_PATH));
|
||||
$app->getKernel()->setEnvironment(Kernel::LIVE);
|
||||
|
||||
// Test being logged in as admin
|
||||
$chain = new ErrorControlChainMiddleware($app);
|
||||
$request = new HTTPRequest('GET', '/', ['flush' => 1]);
|
||||
$request->setSession(new Session(['loggedInAs' => $adminID]));
|
||||
$result = $chain->process($request, function () {
|
||||
return null;
|
||||
});
|
||||
|
||||
$this->assertInstanceOf(HTTPResponse::class, $result);
|
||||
$location = $result->getHeader('Location');
|
||||
$this->assertContains('?flush=1&flushtoken=', $location);
|
||||
$this->assertNotContains('Security/login', $location);
|
||||
}
|
||||
|
||||
public function testLiveFlushUnauthenticated()
|
||||
{
|
||||
// Mock app
|
||||
$app = new HTTPApplication(new BlankKernel(BASE_PATH));
|
||||
$app->getKernel()->setEnvironment(Kernel::LIVE);
|
||||
|
||||
// Test being logged in as no one
|
||||
Security::setCurrentUser(null);
|
||||
$chain = new ErrorControlChainMiddleware($app);
|
||||
$request = new HTTPRequest('GET', '/', ['flush' => 1]);
|
||||
$request->setSession(new Session(['loggedInAs' => 0]));
|
||||
$result = $chain->process($request, function () {
|
||||
return null;
|
||||
});
|
||||
|
||||
// Should be directed to login, not to flush
|
||||
$this->assertInstanceOf(HTTPResponse::class, $result);
|
||||
$location = $result->getHeader('Location');
|
||||
$this->assertNotContains('?flush=1&flushtoken=', $location);
|
||||
$this->assertContains('Security/login', $location);
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Tests\Startup\ErrorControlChainMiddlewareTest;
|
||||
|
||||
use SilverStripe\Core\CoreKernel;
|
||||
|
||||
class BlankKernel extends CoreKernel
|
||||
{
|
||||
public function __construct($basePath)
|
||||
{
|
||||
// Noop
|
||||
}
|
||||
|
||||
public function boot($flush = false)
|
||||
{
|
||||
// Noop
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user