-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[HttpFoundation] MongoDbSessionHandler::read() now checks for valid session age #12516
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -40,6 +40,7 @@ protected function setUp() | |
'id_field' => '_id', | ||
'data_field' => 'data', | ||
'time_field' => 'time', | ||
'expiry_field' => 'expires_at', | ||
'database' => 'sf2-test', | ||
'collection' => 'session-test', | ||
); | ||
|
@@ -73,7 +74,7 @@ public function testCloseMethodAlwaysReturnTrue() | |
$this->assertTrue($this->storage->close(), 'The "close" method should always return true'); | ||
} | ||
|
||
public function testWrite() | ||
public function testRead() | ||
{ | ||
$collection = $this->createMongoCollectionMock(); | ||
|
||
|
@@ -83,36 +84,34 @@ public function testWrite() | |
->will($this->returnValue($collection)); | ||
|
||
$that = $this; | ||
$data = array(); | ||
|
||
$collection->expects($this->once()) | ||
->method('update') | ||
->will($this->returnCallback(function ($criteria, $updateData, $options) use ($that, &$data) { | ||
$that->assertEquals(array($that->options['id_field'] => 'foo'), $criteria); | ||
$that->assertEquals(array('upsert' => true, 'multiple' => false), $options); | ||
// defining the timeout before the actual method call | ||
// allows to test for "greater than" values in the $criteria | ||
$testTimeout = time(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. just one more reason to "mock" it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The tests don't talk to the actual database-layer. So everything is relative to PHP's timezone, see above. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. time() + (int) ini_get('session.gc_maxlifetime') is just untestable. but this is related to a bunch of handlers so it shouldn't keep us off to merge the PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, this is caused by PHP's not-very-elegant session API. The only proper way around this would be a custom session system, ignoring both native API and configuration for the session-module. |
||
|
||
$data = $updateData['$set']; | ||
$collection->expects($this->once()) | ||
->method('findOne') | ||
->will($this->returnCallback(function ($criteria) use ($that, $testTimeout) { | ||
$that->assertArrayHasKey($that->options['id_field'], $criteria); | ||
$that->assertEquals($criteria[$that->options['id_field']], 'foo'); | ||
|
||
$that->assertArrayHasKey($that->options['expiry_field'], $criteria); | ||
$that->assertArrayHasKey('$gte', $criteria[$that->options['expiry_field']]); | ||
$that->assertInstanceOf('MongoDate', $criteria[$that->options['expiry_field']]['$gte']); | ||
$that->assertGreaterThanOrEqual($criteria[$that->options['expiry_field']]['$gte']->sec, $testTimeout); | ||
|
||
return array( | ||
$that->options['id_field'] => 'foo', | ||
$that->options['data_field'] => new \MongoBinData('bar', \MongoBinData::BYTE_ARRAY), | ||
$that->options['id_field'] => new \MongoDate(), | ||
); | ||
})); | ||
|
||
$this->assertTrue($this->storage->write('foo', 'bar')); | ||
|
||
$this->assertEquals('bar', $data[$this->options['data_field']]->bin); | ||
$that->assertInstanceOf('MongoDate', $data[$this->options['time_field']]); | ||
$this->assertEquals('bar', $this->storage->read('foo')); | ||
} | ||
|
||
public function testWriteWhenUsingExpiresField() | ||
public function testWrite() | ||
{ | ||
$this->options = array( | ||
'id_field' => '_id', | ||
'data_field' => 'data', | ||
'time_field' => 'time', | ||
'database' => 'sf2-test', | ||
'collection' => 'session-test', | ||
'expiry_field' => 'expiresAt', | ||
); | ||
|
||
$this->storage = new MongoDbSessionHandler($this->mongo, $this->options); | ||
|
||
$collection = $this->createMongoCollectionMock(); | ||
|
||
$this->mongo->expects($this->once()) | ||
|
@@ -132,11 +131,13 @@ public function testWriteWhenUsingExpiresField() | |
$data = $updateData['$set']; | ||
})); | ||
|
||
$expectedExpiry = time() + (int) ini_get('session.gc_maxlifetime'); | ||
$this->assertTrue($this->storage->write('foo', 'bar')); | ||
|
||
$this->assertEquals('bar', $data[$this->options['data_field']]->bin); | ||
$that->assertInstanceOf('MongoDate', $data[$this->options['time_field']]); | ||
$that->assertInstanceOf('MongoDate', $data[$this->options['expiry_field']]); | ||
$this->assertInstanceOf('MongoDate', $data[$this->options['expiry_field']]); | ||
$this->assertGreaterThanOrEqual($expectedExpiry, $data[$this->options['expiry_field']]->sec); | ||
} | ||
|
||
public function testReplaceSessionData() | ||
|
@@ -192,39 +193,13 @@ public function testGc() | |
$collection->expects($this->once()) | ||
->method('remove') | ||
->will($this->returnCallback(function ($criteria) use ($that) { | ||
$that->assertInstanceOf('MongoDate', $criteria[$that->options['time_field']]['$lt']); | ||
$that->assertGreaterThanOrEqual(time() - 1, $criteria[$that->options['time_field']]['$lt']->sec); | ||
$that->assertInstanceOf('MongoDate', $criteria[$that->options['expiry_field']]['$lt']); | ||
$that->assertGreaterThanOrEqual(time() - 1, $criteria[$that->options['expiry_field']]['$lt']->sec); | ||
})); | ||
|
||
$this->assertTrue($this->storage->gc(1)); | ||
} | ||
|
||
public function testGcWhenUsingExpiresField() | ||
{ | ||
$this->options = array( | ||
'id_field' => '_id', | ||
'data_field' => 'data', | ||
'time_field' => 'time', | ||
'database' => 'sf2-test', | ||
'collection' => 'session-test', | ||
'expiry_field' => 'expiresAt', | ||
); | ||
|
||
$this->storage = new MongoDbSessionHandler($this->mongo, $this->options); | ||
|
||
$collection = $this->createMongoCollectionMock(); | ||
|
||
$this->mongo->expects($this->never()) | ||
->method('selectCollection'); | ||
|
||
$that = $this; | ||
|
||
$collection->expects($this->never()) | ||
->method('remove'); | ||
|
||
$this->assertTrue($this->storage->gc(1)); | ||
} | ||
|
||
public function testGetConnection() | ||
{ | ||
$method = new \ReflectionMethod($this->storage, 'getMongo'); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should try to inject time() also. correct me if i am wrong, but this assumes that your webserver and your database server are sharing the same timezome?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's been this way. I think this should be a separate issue?
Edit: MongoDate is not timezone aware anyway. So this is a non-issue. It's always based on PHP's current default-timezone. It's the driver's responsibility to "translate"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correction, it's based on the timezone of PHP's execution context, see: https://gist.github.com/bzikarsky/c309a55fefbaf9bdaa5d
Anyhow:
ext-mongo
uses the system's timezone to create correctIsodate
-instances in the database. The same is valid for reading.Ignoring PHP's timezone is probably a bug. But that is located in
ext-mongo
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i looked at the other session handlers, this issue is related to most of them. to solve this issue symfony should provide a service that is able to return date / time informations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would disagree. The BSON date type is specified as:
In your gist,
strtotime()
happens to respect PHP's timezone, as doesdate('c')
when you print out the dates later; however, when printing the date on the way out of the database, you're ignoring the original timezone from thetz
field. PHP will simply use the UNIX timestamp from the MongoDate and render it in the default timezone. This is why the time offsets of the three records are still staggered (0, +2, and +1 respectively). Does that make sense?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to avoid any more confusion: I'm fine with the output of my exmaple-script. This is what I expected. My comment about there being a bug in
ext-mongo
was related to me falling back to the usage ofTZ=/usr/share/zoneinfo...
. But I must have tricked myself somewhere.I was under the impression that
ext-mongo
converts the UNIX timestamps to Isodates (in UTC) based on theTZ
process context, and ignores PHP's internaldate.timezone
. That's the reason why I went for a bash-script instead of a PHP script in the beginning.But I ran a 2nd test today morning, and this time it worked flawlessly. Strange. Modified Gist at https://gist.github.com/bzikarsky/4044d1fa37e32e5c6996
Anyhow I think everything is fine - no bugs anywhere to be seen. 😉