Merge branch '4.2' into 4

This commit is contained in:
Daniel Hensby 2018-07-23 17:42:43 +01:00
commit cc0320b2a7
No known key found for this signature in database
GPG Key ID: D8DEBC4C8E7BC8B9
13 changed files with 538 additions and 49 deletions

View File

@ -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

View File

@ -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();
}
}
}

View File

@ -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

View File

@ -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;
}
}

View File

@ -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",

View File

@ -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;

View File

@ -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

View File

@ -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;
}

View File

@ -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');
}
}

View File

@ -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

View File

@ -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']);
}
}

View File

@ -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');
}
}

View File

@ -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);
}
}