mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 12:05:37 +00:00
Merge branch '4.2' into 4
This commit is contained in:
commit
cc0320b2a7
@ -206,7 +206,7 @@ class Director implements TemplateGlobalProvider
|
||||
if ($session instanceof Session) {
|
||||
// Note: If passing $session as object, ensure that changes are written back
|
||||
// This is important for classes such as FunctionalTest which emulate cross-request persistence
|
||||
$newVars['_SESSION'] = $sessionArray = $session->getAll();
|
||||
$newVars['_SESSION'] = $sessionArray = $session->getAll() ?: [];
|
||||
$finally[] = function () use ($session, $sessionArray) {
|
||||
if (isset($_SESSION)) {
|
||||
// Set new / updated keys
|
||||
|
@ -26,8 +26,6 @@ class HTTPCacheControlMiddleware implements HTTPMiddleware, Resettable
|
||||
|
||||
const STATE_DISABLED = 'disabled';
|
||||
|
||||
const STATE_DEFAULT = 'default';
|
||||
|
||||
/**
|
||||
* Generate response for the given request
|
||||
*
|
||||
@ -90,11 +88,9 @@ class HTTPCacheControlMiddleware implements HTTPMiddleware, Resettable
|
||||
'must-revalidate' => true,
|
||||
],
|
||||
self::STATE_ENABLED => [
|
||||
'must-revalidate' => true,
|
||||
],
|
||||
self::STATE_DEFAULT => [
|
||||
'no-cache' => true,
|
||||
],
|
||||
'must-revalidate' => true,
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
@ -103,7 +99,7 @@ class HTTPCacheControlMiddleware implements HTTPMiddleware, Resettable
|
||||
* @config
|
||||
* @var string
|
||||
*/
|
||||
private static $defaultState = self::STATE_DEFAULT;
|
||||
private static $defaultState = self::STATE_ENABLED;
|
||||
|
||||
/**
|
||||
* Current state
|
||||
@ -498,7 +494,13 @@ class HTTPCacheControlMiddleware implements HTTPMiddleware, Resettable
|
||||
{
|
||||
// Affect all non-disabled states
|
||||
$applyTo = [self::STATE_ENABLED, self::STATE_PRIVATE, self::STATE_PUBLIC];
|
||||
$this->setStateDirective($applyTo, 'no-cache', $noCache);
|
||||
if ($noCache) {
|
||||
$this->setStateDirective($applyTo, 'no-cache');
|
||||
$this->removeStateDirective($applyTo, 'max-age');
|
||||
$this->removeStateDirective($applyTo, 's-maxage');
|
||||
} else {
|
||||
$this->removeStateDirective($applyTo, 'no-cache');
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -515,6 +517,10 @@ class HTTPCacheControlMiddleware implements HTTPMiddleware, Resettable
|
||||
// Affect all non-disabled states
|
||||
$applyTo = [self::STATE_ENABLED, self::STATE_PRIVATE, self::STATE_PUBLIC];
|
||||
$this->setStateDirective($applyTo, 'max-age', $age);
|
||||
if ($age) {
|
||||
$this->removeStateDirective($applyTo, 'no-cache');
|
||||
$this->removeStateDirective($applyTo, 'no-store');
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -531,6 +537,10 @@ class HTTPCacheControlMiddleware implements HTTPMiddleware, Resettable
|
||||
// Affect all non-disabled states
|
||||
$applyTo = [self::STATE_ENABLED, self::STATE_PRIVATE, self::STATE_PUBLIC];
|
||||
$this->setStateDirective($applyTo, 's-maxage', $age);
|
||||
if ($age) {
|
||||
$this->removeStateDirective($applyTo, 'no-cache');
|
||||
$this->removeStateDirective($applyTo, 'no-store');
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -763,17 +773,16 @@ class HTTPCacheControlMiddleware implements HTTPMiddleware, Resettable
|
||||
*/
|
||||
protected function augmentState(HTTPRequest $request, HTTPResponse $response)
|
||||
{
|
||||
// If sessions exist we assume that the responses should not be cached by CDNs / proxies as we are
|
||||
// likely to be supplying information relevant to the current user only
|
||||
if ($request->getSession()->getAll()) {
|
||||
// Don't force in case user code chooses to opt in to public caching
|
||||
$this->privateCache();
|
||||
}
|
||||
|
||||
// Errors disable cache (unless some errors are cached intentionally by usercode)
|
||||
if ($response->isError() || $response->isRedirect()) {
|
||||
// Even if publicCache(true) is specified, errors will be uncacheable
|
||||
$this->disableCache(true);
|
||||
} elseif ($request->getSession()->getAll()) {
|
||||
// If sessions exist we assume that the responses should not be cached by CDNs / proxies as we are
|
||||
// likely to be supplying information relevant to the current user only
|
||||
|
||||
// Don't force in case user code chooses to opt in to public caching
|
||||
$this->privateCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,6 +49,9 @@ use SilverStripe\Dev\Deprecation;
|
||||
*
|
||||
* Once you have saved a value to the Session you can access it by using the {@link Session::get()} function.
|
||||
* Like the {@link Session::set()} function you can use this anywhere in your PHP files.
|
||||
* Note that session data isn't persisted in PHP's own session store (via $_SESSION)
|
||||
* until {@link Session::save()} is called, which happens automatically at the end of a standard request
|
||||
* through {@link SilverStripe\Control\Middleware\SessionMiddleware}.
|
||||
*
|
||||
* The values in the comments are the values stored from the previous example.
|
||||
*
|
||||
@ -84,7 +87,6 @@ use SilverStripe\Dev\Deprecation;
|
||||
* </code>
|
||||
*
|
||||
* @see Cookie
|
||||
* @todo This class is currently really basic and could do with a more well-thought-out implementation.
|
||||
*/
|
||||
class Session
|
||||
{
|
||||
@ -128,6 +130,12 @@ class Session
|
||||
*/
|
||||
private static $cookie_secure = false;
|
||||
|
||||
/**
|
||||
* @config
|
||||
* @var string
|
||||
*/
|
||||
private static $cookie_name_secure = 'SECSESSID';
|
||||
|
||||
/**
|
||||
* Name of session cache limiter to use.
|
||||
* Defaults to '' to disable cache limiter entirely.
|
||||
@ -145,6 +153,11 @@ class Session
|
||||
*/
|
||||
protected $data = null;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $started = false;
|
||||
|
||||
/**
|
||||
* List of keys changed. This is a nested array which represents the
|
||||
* keys modified in $this->data. The value of each item is either "true"
|
||||
@ -192,16 +205,21 @@ class Session
|
||||
}
|
||||
|
||||
$this->data = $data;
|
||||
$this->started = isset($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Init this session instance before usage
|
||||
* Init this session instance before usage,
|
||||
* if a session identifier is part of the passed in request.
|
||||
* Otherwise, a session might be started in {@link save()}
|
||||
* if session data needs to be written with a new session identifier.
|
||||
*
|
||||
* @param HTTPRequest $request
|
||||
*/
|
||||
public function init(HTTPRequest $request)
|
||||
{
|
||||
if (!$this->isStarted()) {
|
||||
|
||||
if (!$this->isStarted() && $this->requestContainsSessionId($request)) {
|
||||
$this->start($request);
|
||||
}
|
||||
|
||||
@ -210,6 +228,7 @@ class Session
|
||||
if ($this->data['HTTP_USER_AGENT'] !== $this->userAgent($request)) {
|
||||
$this->clearAll();
|
||||
$this->destroy();
|
||||
$this->started = false;
|
||||
$this->start($request);
|
||||
}
|
||||
}
|
||||
@ -233,11 +252,24 @@ class Session
|
||||
*/
|
||||
public function isStarted()
|
||||
{
|
||||
return isset($this->data);
|
||||
return $this->started;
|
||||
}
|
||||
|
||||
/**
|
||||
* Begin session
|
||||
* @param HTTPRequest $request
|
||||
* @return bool
|
||||
*/
|
||||
public function requestContainsSessionId(HTTPRequest $request)
|
||||
{
|
||||
$secure = Director::is_https($request) && $this->config()->get('cookie_secure');
|
||||
$name = $secure ? $this->config()->get('cookie_name_secure') : session_name();
|
||||
return (bool)Cookie::get($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Begin session, regardless if a session identifier is present in the request,
|
||||
* or whether any session data needs to be written.
|
||||
* See {@link init()} if you want to "lazy start" a session.
|
||||
*
|
||||
* @param HTTPRequest $request The request for which to start a session
|
||||
*/
|
||||
@ -281,7 +313,7 @@ class Session
|
||||
// If we want a secure cookie for HTTPS, use a seperate session name. This lets us have a
|
||||
// seperate (less secure) session for non-HTTPS requests
|
||||
if ($secure) {
|
||||
session_name('SECSESSID');
|
||||
session_name($this->config()->get('cookie_name_secure'));
|
||||
}
|
||||
|
||||
$limiter = $this->config()->get('sessionCacheLimiter');
|
||||
@ -291,7 +323,16 @@ class Session
|
||||
|
||||
session_start();
|
||||
|
||||
$this->data = isset($_SESSION) ? $_SESSION : array();
|
||||
if (isset($_SESSION)) {
|
||||
// Initialise data from session store if present
|
||||
$data = $_SESSION;
|
||||
// Merge in existing in-memory data, taking priority over session store data
|
||||
$this->recursivelyApply((array)$this->data, $data);
|
||||
} else {
|
||||
// Use in-memory data if the session is lazy started
|
||||
$data = $this->data;
|
||||
}
|
||||
$this->data = $data ?: [];
|
||||
} else {
|
||||
$this->data = [];
|
||||
}
|
||||
@ -302,6 +343,8 @@ class Session
|
||||
Cookie::set(session_name(), session_id(), $timeout/86400, $path, $domain ? $domain
|
||||
: null, $secure, true);
|
||||
}
|
||||
|
||||
$this->started = true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -335,9 +378,6 @@ class Session
|
||||
*/
|
||||
public function set($name, $val)
|
||||
{
|
||||
if (!$this->isStarted()) {
|
||||
throw new BadMethodCallException("Session cannot be modified until it's started");
|
||||
}
|
||||
$var = &$this->nestedValueRef($name, $this->data);
|
||||
|
||||
// Mark changed
|
||||
@ -380,10 +420,6 @@ class Session
|
||||
*/
|
||||
public function addToArray($name, $val)
|
||||
{
|
||||
if (!$this->isStarted()) {
|
||||
throw new BadMethodCallException("Session cannot be modified until it's started");
|
||||
}
|
||||
|
||||
$names = explode('.', $name);
|
||||
|
||||
// We still want to do this even if we have strict path checking for legacy code
|
||||
@ -407,9 +443,6 @@ class Session
|
||||
*/
|
||||
public function get($name)
|
||||
{
|
||||
if (!$this->isStarted()) {
|
||||
throw new BadMethodCallException("Session cannot be accessed until it's started");
|
||||
}
|
||||
return $this->nestedValue($name, $this->data);
|
||||
}
|
||||
|
||||
@ -421,10 +454,6 @@ class Session
|
||||
*/
|
||||
public function clear($name)
|
||||
{
|
||||
if (!$this->isStarted()) {
|
||||
throw new BadMethodCallException("Session cannot be modified until it's started");
|
||||
}
|
||||
|
||||
// Get var by path
|
||||
$var = $this->nestedValue($name, $this->data);
|
||||
|
||||
@ -449,10 +478,6 @@ class Session
|
||||
*/
|
||||
public function clearAll()
|
||||
{
|
||||
if (!$this->isStarted()) {
|
||||
throw new BadMethodCallException("Session cannot be modified until it's started");
|
||||
}
|
||||
|
||||
if ($this->data && is_array($this->data)) {
|
||||
foreach (array_keys($this->data) as $key) {
|
||||
$this->clear($key);
|
||||
@ -495,7 +520,7 @@ class Session
|
||||
$this->start($request);
|
||||
}
|
||||
|
||||
// Apply all changes recursively
|
||||
// Apply all changes recursively, implicitly writing them to the actual PHP session store.
|
||||
$this->recursivelyApplyChanges($this->changedData, $this->data, $_SESSION);
|
||||
}
|
||||
}
|
||||
@ -590,6 +615,7 @@ class Session
|
||||
*/
|
||||
protected function recursivelyApplyChanges($changes, $source, &$destination)
|
||||
{
|
||||
$source = $source ?: [];
|
||||
foreach ($changes as $key => $changed) {
|
||||
if ($changed === true) {
|
||||
// Determine if replacement or removal
|
||||
|
@ -45,6 +45,7 @@ class Backtrace
|
||||
array('SilverStripe\\Security\\PasswordEncryptor_MySQLOldPassword', 'salt'),
|
||||
array('SilverStripe\\Security\\PasswordEncryptor_Blowfish', 'encrypt'),
|
||||
array('SilverStripe\\Security\\PasswordEncryptor_Blowfish', 'salt'),
|
||||
array('*', 'updateValidatePassword'),
|
||||
);
|
||||
|
||||
/**
|
||||
@ -106,7 +107,10 @@ class Backtrace
|
||||
$match = false;
|
||||
if (!empty($bt[$i]['class'])) {
|
||||
foreach ($ignoredArgs as $fnSpec) {
|
||||
if (is_array($fnSpec) && $bt[$i]['class'] == $fnSpec[0] && $bt[$i]['function'] == $fnSpec[1]) {
|
||||
if (is_array($fnSpec) &&
|
||||
('*' == $fnSpec[0] || $bt[$i]['class'] == $fnSpec[0]) &&
|
||||
$bt[$i]['function'] == $fnSpec[1]
|
||||
) {
|
||||
$match = true;
|
||||
}
|
||||
}
|
||||
|
@ -384,6 +384,13 @@ class InstallRequirements
|
||||
'SimpleXML support not included in PHP.'
|
||||
));
|
||||
|
||||
// Check for INTL support
|
||||
$this->requireClass('IntlTimeZone', array(
|
||||
'PHP Configuration',
|
||||
'Intl support',
|
||||
'Internationalization (php-intl) support not included in PHP.'
|
||||
));
|
||||
|
||||
// Check for token_get_all
|
||||
$this->requireFunction('token_get_all', array(
|
||||
"PHP Configuration",
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace SilverStripe\Forms;
|
||||
|
||||
use LogicException;
|
||||
use ReflectionClass;
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Control\RequestHandler;
|
||||
@ -355,14 +356,20 @@ class FormField extends RequestHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a link to this field.
|
||||
* Return a link to this field
|
||||
*
|
||||
* @param string $action
|
||||
*
|
||||
* @return string
|
||||
* @throws LogicException If no form is set yet
|
||||
*/
|
||||
public function Link($action = null)
|
||||
{
|
||||
if (!$this->form) {
|
||||
throw new LogicException(
|
||||
'Field must be associated with a form to call Link(). Please use $field->setForm($form);'
|
||||
);
|
||||
}
|
||||
|
||||
$link = Controller::join_links($this->form->FormAction(), 'field/' . $this->name, $action);
|
||||
$this->extend('updateLink', $link, $action);
|
||||
return $link;
|
||||
|
@ -424,6 +424,10 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity
|
||||
// Get duplicates
|
||||
if ($relations === null) {
|
||||
$relations = $this->config()->get('cascade_duplicates');
|
||||
// Remove any duplicate entries before duplicating them
|
||||
if (is_array($relations)) {
|
||||
$relations = array_unique($relations);
|
||||
}
|
||||
}
|
||||
|
||||
// Create unsaved raw duplicate
|
||||
|
@ -45,9 +45,16 @@ class SessionAuthenticationHandler implements AuthenticationHandler
|
||||
*/
|
||||
public function authenticateRequest(HTTPRequest $request)
|
||||
{
|
||||
$session = $request->getSession();
|
||||
|
||||
// Sessions are only started when a session cookie is detected
|
||||
if (!$session->isStarted()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// If ID is a bad ID it will be treated as if the user is not logged in, rather than throwing a
|
||||
// ValidationException
|
||||
$id = $request->getSession()->get($this->getSessionVariable());
|
||||
$id = $session->get($this->getSessionVariable());
|
||||
if (!$id) {
|
||||
return null;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace SilverStripe\Control\Tests\Middleware;
|
||||
|
||||
use SilverStripe\Control\HTTPResponse;
|
||||
use SilverStripe\Control\Middleware\HTTPCacheControlMiddleware;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
|
||||
@ -12,15 +13,177 @@ class HTTPCacheControlMiddlewareTest extends SapphireTest
|
||||
parent::setUp();
|
||||
// Set to disabled at null forcing level
|
||||
HTTPCacheControlMiddleware::config()
|
||||
->set('defaultState', 'disabled')
|
||||
->set('defaultState', HTTPCacheControlMiddleware::STATE_ENABLED)
|
||||
->set('defaultForcingLevel', 0);
|
||||
HTTPCacheControlMiddleware::reset();
|
||||
}
|
||||
|
||||
public function provideCacheStates()
|
||||
{
|
||||
return [
|
||||
['enableCache', false],
|
||||
['publicCache', false],
|
||||
['privateCache', false],
|
||||
['disableCache', true],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideCacheStates
|
||||
*/
|
||||
public function testCheckDefaultStates($state, $immutable)
|
||||
{
|
||||
$cc = HTTPCacheControlMiddleware::singleton();
|
||||
$cc->{$state}();
|
||||
|
||||
$response = new HTTPResponse();
|
||||
$cc->applyToResponse($response);
|
||||
|
||||
$this->assertContains('must-revalidate', $response->getHeader('cache-control'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideCacheStates
|
||||
*/
|
||||
public function testSetMaxAge($state, $immutable)
|
||||
{
|
||||
$cc = HTTPCacheControlMiddleware::singleton();
|
||||
$cc->{$state}();
|
||||
|
||||
$originalResponse = new HTTPResponse();
|
||||
$cc->applyToResponse($originalResponse);
|
||||
|
||||
$cc->setMaxAge('300');
|
||||
|
||||
$response = new HTTPResponse();
|
||||
|
||||
$cc->applyToResponse($response);
|
||||
|
||||
if ($immutable) {
|
||||
$this->assertEquals($originalResponse->getHeader('cache-control'), $response->getHeader('cache-control'));
|
||||
} else {
|
||||
$this->assertContains('max-age=300', $response->getHeader('cache-control'));
|
||||
$this->assertNotContains('no-cache', $response->getHeader('cache-control'));
|
||||
$this->assertNotContains('no-store', $response->getHeader('cache-control'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideCacheStates
|
||||
*/
|
||||
public function testSetNoStore($state, $immutable)
|
||||
{
|
||||
$cc = HTTPCacheControlMiddleware::singleton();
|
||||
$cc->setMaxAge('300');
|
||||
$cc->setSharedMaxAge('300');
|
||||
|
||||
$cc->{$state}();
|
||||
|
||||
$originalResponse = new HTTPResponse();
|
||||
$cc->applyToResponse($originalResponse);
|
||||
|
||||
$cc->setNoStore();
|
||||
|
||||
$response = new HTTPResponse();
|
||||
|
||||
$cc->applyToResponse($response);
|
||||
|
||||
if ($immutable) {
|
||||
$this->assertEquals($originalResponse->getHeader('cache-control'), $response->getHeader('cache-control'));
|
||||
} else {
|
||||
$this->assertContains('no-store', $response->getHeader('cache-control'));
|
||||
$this->assertNotContains('max-age', $response->getHeader('cache-control'));
|
||||
$this->assertNotContains('s-maxage', $response->getHeader('cache-control'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideCacheStates
|
||||
*/
|
||||
public function testSetNoCache($state, $immutable)
|
||||
{
|
||||
$cc = HTTPCacheControlMiddleware::singleton();
|
||||
$cc->setMaxAge('300');
|
||||
$cc->setSharedMaxAge('300');
|
||||
|
||||
$cc->{$state}();
|
||||
|
||||
$originalResponse = new HTTPResponse();
|
||||
$cc->applyToResponse($originalResponse);
|
||||
|
||||
$cc->setNoCache();
|
||||
|
||||
$response = new HTTPResponse();
|
||||
|
||||
$cc->applyToResponse($response);
|
||||
|
||||
if ($immutable) {
|
||||
$this->assertEquals($originalResponse->getHeader('cache-control'), $response->getHeader('cache-control'));
|
||||
} else {
|
||||
$this->assertContains('no-cache', $response->getHeader('cache-control'));
|
||||
$this->assertNotContains('max-age', $response->getHeader('cache-control'));
|
||||
$this->assertNotContains('s-maxage', $response->getHeader('cache-control'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideCacheStates
|
||||
*/
|
||||
public function testSetSharedMaxAge($state, $immutable)
|
||||
{
|
||||
$cc = HTTPCacheControlMiddleware::singleton();
|
||||
|
||||
$cc->{$state}();
|
||||
|
||||
$originalResponse = new HTTPResponse();
|
||||
$cc->applyToResponse($originalResponse);
|
||||
|
||||
$cc->setSharedMaxAge('300');
|
||||
|
||||
$response = new HTTPResponse();
|
||||
|
||||
$cc->applyToResponse($response);
|
||||
|
||||
if ($immutable) {
|
||||
$this->assertEquals($originalResponse->getHeader('cache-control'), $response->getHeader('cache-control'));
|
||||
} else {
|
||||
$this->assertContains('s-maxage=300', $response->getHeader('cache-control'));
|
||||
$this->assertNotContains('no-cache', $response->getHeader('cache-control'));
|
||||
$this->assertNotContains('no-store', $response->getHeader('cache-control'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideCacheStates
|
||||
*/
|
||||
public function testSetMustRevalidate($state, $immutable)
|
||||
{
|
||||
$cc = HTTPCacheControlMiddleware::singleton();
|
||||
|
||||
$cc->{$state}();
|
||||
|
||||
$originalResponse = new HTTPResponse();
|
||||
$cc->applyToResponse($originalResponse);
|
||||
|
||||
$cc->setMustRevalidate();
|
||||
|
||||
$response = new HTTPResponse();
|
||||
|
||||
$cc->applyToResponse($response);
|
||||
|
||||
if ($immutable) {
|
||||
$this->assertEquals($originalResponse->getHeader('cache-control'), $response->getHeader('cache-control'));
|
||||
} else {
|
||||
$this->assertContains('must-revalidate', $response->getHeader('cache-control'));
|
||||
$this->assertNotContains('max-age', $response->getHeader('cache-control'));
|
||||
$this->assertNotContains('s-maxage', $response->getHeader('cache-control'));
|
||||
}
|
||||
}
|
||||
|
||||
public function testCachingPriorities()
|
||||
{
|
||||
$hcc = new HTTPCacheControlMiddleware();
|
||||
$this->assertTrue($this->isDisabled($hcc), 'caching starts as disabled');
|
||||
$this->assertFalse($this->isDisabled($hcc), 'caching starts as disabled');
|
||||
|
||||
$hcc->enableCache();
|
||||
$this->assertFalse($this->isDisabled($hcc));
|
||||
@ -74,6 +237,6 @@ class HTTPCacheControlMiddlewareTest extends SapphireTest
|
||||
|
||||
protected function isDisabled(HTTPCacheControlMiddleware $hcc)
|
||||
{
|
||||
return $hcc->hasDirective('no-cache') && !$hcc->hasDirective('private') && !$hcc->hasDirective('public');
|
||||
return $hcc->hasDirective('no-store') && !$hcc->hasDirective('private') && !$hcc->hasDirective('public');
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
namespace SilverStripe\Control\Tests;
|
||||
|
||||
use http\Exception\BadMessageException;
|
||||
use SilverStripe\Control\Cookie;
|
||||
use SilverStripe\Control\Session;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\Control\HTTPRequest;
|
||||
@ -22,6 +24,127 @@ class SessionTest extends SapphireTest
|
||||
return parent::setUp();
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
*/
|
||||
public function testInitDoesNotStartSessionWithoutIdentifier()
|
||||
{
|
||||
$req = new HTTPRequest('GET', '/');
|
||||
$session = new Session(null); // unstarted session
|
||||
$session->init($req);
|
||||
$this->assertFalse($session->isStarted());
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
*/
|
||||
public function testInitStartsSessionWithIdentifier()
|
||||
{
|
||||
$req = new HTTPRequest('GET', '/');
|
||||
Cookie::set(session_name(), '1234');
|
||||
$session = new Session(null); // unstarted session
|
||||
$session->init($req);
|
||||
$this->assertTrue($session->isStarted());
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
*/
|
||||
public function testInitStartsSessionWithData()
|
||||
{
|
||||
$req = new HTTPRequest('GET', '/');
|
||||
$session = new Session([]);
|
||||
$session->init($req);
|
||||
$this->assertTrue($session->isStarted());
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
*/
|
||||
public function testStartUsesDefaultCookieNameWithHttp()
|
||||
{
|
||||
$req = (new HTTPRequest('GET', '/'))
|
||||
->setScheme('http');
|
||||
Cookie::set(session_name(), '1234');
|
||||
$session = new Session(null); // unstarted session
|
||||
$session->start($req);
|
||||
$this->assertNotEquals(session_name(), $session->config()->get('cookie_name_secure'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
*/
|
||||
public function testStartUsesDefaultCookieNameWithHttpsAndCookieSecureOff()
|
||||
{
|
||||
$req = (new HTTPRequest('GET', '/'))
|
||||
->setScheme('https');
|
||||
Cookie::set(session_name(), '1234');
|
||||
$session = new Session(null); // unstarted session
|
||||
$session->start($req);
|
||||
$this->assertNotEquals(session_name(), $session->config()->get('cookie_name_secure'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
*/
|
||||
public function testStartUsesSecureCookieNameWithHttpsAndCookieSecureOn()
|
||||
{
|
||||
$req = (new HTTPRequest('GET', '/'))
|
||||
->setScheme('https');
|
||||
Cookie::set(session_name(), '1234');
|
||||
$session = new Session(null); // unstarted session
|
||||
$session->config()->update('cookie_secure', true);
|
||||
$session->start($req);
|
||||
$this->assertEquals(session_name(), $session->config()->get('cookie_name_secure'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
* @expectedException BadMethodCallException
|
||||
* @expectedExceptionMessage Session has already started
|
||||
*/
|
||||
public function testStartErrorsWhenStartingTwice()
|
||||
{
|
||||
$req = new HTTPRequest('GET', '/');
|
||||
$session = new Session(null); // unstarted session
|
||||
$session->start($req);
|
||||
$session->start($req);
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
*/
|
||||
public function testStartRetainsInMemoryData()
|
||||
{
|
||||
$this->markTestIncomplete('Test');
|
||||
// TODO Figure out how to simulate session vars without a session_start() resetting them
|
||||
// $_SESSION['existing'] = true;
|
||||
// $_SESSION['merge'] = 1;
|
||||
$req = new HTTPRequest('GET', '/');
|
||||
$session = new Session(null); // unstarted session
|
||||
$session->set('new', true);
|
||||
$session->set('merge', 2);
|
||||
$session->start($req); // simulate lazy start
|
||||
$this->assertEquals(
|
||||
[
|
||||
// 'existing' => true,
|
||||
'new' => true,
|
||||
'merge' => 2
|
||||
],
|
||||
$session->getAll()
|
||||
);
|
||||
|
||||
unset($_SESSION);
|
||||
}
|
||||
|
||||
public function testGetSetBasics()
|
||||
{
|
||||
$this->session->set('Test', 'Test');
|
||||
@ -124,6 +247,25 @@ class SessionTest extends SapphireTest
|
||||
);
|
||||
}
|
||||
|
||||
public function testRequestContainsSessionId()
|
||||
{
|
||||
$req = new HTTPRequest('GET', '/');
|
||||
$session = new Session(null); // unstarted session
|
||||
$this->assertFalse($session->requestContainsSessionId($req));
|
||||
Cookie::set(session_name(), '1234');
|
||||
$this->assertTrue($session->requestContainsSessionId($req));
|
||||
}
|
||||
|
||||
public function testRequestContainsSessionIdRespectsCookieNameSecure()
|
||||
{
|
||||
$req = (new HTTPRequest('GET', '/'))
|
||||
->setScheme('https');
|
||||
$session = new Session(null); // unstarted session
|
||||
Cookie::set($session->config()->get('cookie_name_secure'), '1234');
|
||||
$session->config()->update('cookie_secure', true);
|
||||
$this->assertTrue($session->requestContainsSessionId($req));
|
||||
}
|
||||
|
||||
public function testUserAgentLockout()
|
||||
{
|
||||
// Set a user agent
|
||||
|
@ -68,4 +68,45 @@ class BacktraceTest extends SapphireTest
|
||||
$this->assertEquals('<filtered>', $filtered[1]['args']['password'], 'Filters class functions');
|
||||
$this->assertEquals('myval', $filtered[2]['args']['myarg'], 'Doesnt filter other functions');
|
||||
}
|
||||
|
||||
public function testFilteredWildCard()
|
||||
{
|
||||
$bt = array(
|
||||
array(
|
||||
'type' => '->',
|
||||
'file' => 'MyFile.php',
|
||||
'line' => 99,
|
||||
'function' => 'myIgnoredGlobalFunction',
|
||||
'args' => array('password' => 'secred',)
|
||||
),
|
||||
array(
|
||||
'class' => 'MyClass',
|
||||
'type' => '->',
|
||||
'file' => 'MyFile.php',
|
||||
'line' => 99,
|
||||
'function' => 'myIgnoredClassFunction',
|
||||
'args' => array('password' => 'secred',)
|
||||
),
|
||||
array(
|
||||
'class' => 'MyClass',
|
||||
'type' => '->',
|
||||
'file' => 'MyFile.php',
|
||||
'line' => 99,
|
||||
'function' => 'myFunction',
|
||||
'args' => array('myarg' => 'myval')
|
||||
)
|
||||
);
|
||||
Backtrace::config()->update(
|
||||
'ignore_function_args',
|
||||
array(
|
||||
array('*', 'myIgnoredClassFunction'),
|
||||
)
|
||||
);
|
||||
|
||||
$filtered = Backtrace::filter_backtrace($bt);
|
||||
|
||||
$this->assertEquals('secred', $filtered[0]['args']['password']);
|
||||
$this->assertEquals('<filtered>', $filtered[1]['args']['password']);
|
||||
$this->assertEquals('myval', $filtered[2]['args']['myarg']);
|
||||
}
|
||||
}
|
||||
|
@ -384,4 +384,22 @@ class FormFieldTest extends SapphireTest
|
||||
$this->assertFalse($field->hasClass('banana'));
|
||||
$this->assertTrue($field->hasClass('cool-BAnana'));
|
||||
}
|
||||
|
||||
public function testLinkWithForm()
|
||||
{
|
||||
$field = new FormField('Test');
|
||||
$form = new Form(null, 'Test', new FieldList, new FieldList);
|
||||
$form->setFormAction('foo');
|
||||
$field->setForm($form);
|
||||
$this->assertSame('foo/field/Test/bar', $field->Link('bar'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \LogicException
|
||||
*/
|
||||
public function testLinkWithoutForm()
|
||||
{
|
||||
$field = new FormField('Test');
|
||||
$field->Link('bar');
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
namespace SilverStripe\Security\Tests\MemberAuthenticator;
|
||||
|
||||
use SilverStripe\Control\Cookie;
|
||||
use SilverStripe\Control\HTTPRequest;
|
||||
use SilverStripe\Control\Session;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
|
||||
use SilverStripe\Security\Member;
|
||||
use SilverStripe\Security\MemberAuthenticator\SessionAuthenticationHandler;
|
||||
|
||||
class SessionAuthenticationHandlerTest extends SapphireTest
|
||||
{
|
||||
protected $usesDatabase = true;
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
*/
|
||||
public function testAuthenticateRequestDefersSessionStartWithoutSessionIdentifier()
|
||||
{
|
||||
$member = new Member(['Email' => 'test@example.com']);
|
||||
$member->write();
|
||||
|
||||
$handler = new SessionAuthenticationHandler();
|
||||
|
||||
$session = new Session(null); // unstarted, simulates lack of session cookie
|
||||
$session->set($handler->getSessionVariable(), $member->ID);
|
||||
|
||||
$req = new HTTPRequest('GET', '/');
|
||||
$req->setSession($session);
|
||||
|
||||
$matchedMember = $handler->authenticateRequest($req);
|
||||
$this->assertNull($matchedMember);
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
*/
|
||||
public function testAuthenticateRequestStartsSessionWithSessionIdentifier()
|
||||
{
|
||||
$member = new Member(['Email' => 'test@example.com']);
|
||||
$member->write();
|
||||
|
||||
$handler = new SessionAuthenticationHandler();
|
||||
|
||||
$session = new Session(null); // unstarted
|
||||
$session->set($handler->getSessionVariable(), $member->ID);
|
||||
|
||||
$req = new HTTPRequest('GET', '/');
|
||||
$req->setSession($session);
|
||||
|
||||
Cookie::set(session_name(), '1234');
|
||||
$session->start($req); // simulate detection of session cookie
|
||||
|
||||
$matchedMember = $handler->authenticateRequest($req);
|
||||
$this->assertNotNull($matchedMember);
|
||||
$this->assertEquals($matchedMember->Email, $member->Email);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user