Skip to content

Commit 234efd3

Browse files
authored
Fix #319: Added support for the 'session.use_strict_mode' ini directive in yii\web\Session
1 parent 20faaf4 commit 234efd3

File tree

7 files changed

+155
-10
lines changed

7 files changed

+155
-10
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
Yii Framework 2 mongodb extension Change Log
22
============================================
33

4-
2.1.10 under development
4+
2.2.0 under development
55
------------------------
66

77
- Bug #308: Fix `yii\mongodb\file\Upload::addFile()` error when uploading file with readonly permissions (sparchatus)
8+
- Enh #319: Added support for the 'session.use_strict_mode' ini directive in `yii\web\Session` (rhertogh)
89

910

1011
2.1.9 November 19, 2019

UPGRADE.md

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,47 @@ if you want to upgrade from version A to version C and there is
88
version B between A and C, you need to following the instructions
99
for both A and B.
1010

11-
Upgrade from Yii 2.0.5
11+
Upgrade from 2.1.9
12+
----------------------
13+
14+
* `yii\mongodb\Session` now respects the 'session.use_strict_mode' ini directive.
15+
In case you use a custom `Session` class and have overwritten the `Session::openSession()` and/or
16+
`Session::writeSession()` functions changes might be required:
17+
* When in strict mode the `openSession()` function should check if the requested session id exists
18+
(and mark it for forced regeneration if not).
19+
For example, the `yii\mongodb\Session` does this at the beginning of the function as follows:
20+
```php
21+
if ($this->getUseStrictMode()) {
22+
$id = $this->getId();
23+
$collection = $this->db->getCollection($this->sessionCollection);
24+
$condition = [
25+
'id' => $id,
26+
'expire' => ['$gt' => time()],
27+
];
28+
if (!$collection->documentExists($condition)) {
29+
//This session id does not exist, mark it for forced regeneration
30+
$this->_forceRegenerateId = $id;
31+
}
32+
}
33+
// ... normal function continues ...
34+
```
35+
* When in strict mode the `writeSession()` function should ignore writing the session under the old id.
36+
For example, the `yii\mongodb\Session` does this at the beginning of the function as follows:
37+
```php
38+
if ($this->getUseStrictMode() && $id === $this->_forceRegenerateId) {
39+
//Ignore write when forceRegenerate is active for this id
40+
return true;
41+
}
42+
// ... normal function continues ...
43+
```
44+
> Note: In case your custom functions call their `parent` functions, there are probably no changes needed to your
45+
code if those parents implement the `useStrictMode` checks.
46+
47+
> Warning: in case `openSession()` and/or `writeSession()` functions do not implement the `useStrictMode` code
48+
the session could be stored under the forced id without warning even if `useStrictMode` is enabled.
49+
50+
51+
Upgrade from 2.0.5
1252
----------------------
1353

1454
* PHP [mongodb](http://php.net/manual/en/set.mongodb.php) extension is now used instead of [mongo](http://php.net/manual/en/book.mongo.php).
@@ -38,13 +78,13 @@ Upgrade from Yii 2.0.5
3878
* Cursor composed via `yii\mongodb\file\Collection::find()` now returns result in the same format as `yii\mongodb\file\Query::one()`.
3979
If you wish to perform file manipulations on returned row you should use `file` key instead of direct method invocations.
4080

41-
Upgrade from Yii 2.0.1
81+
Upgrade from 2.0.1
4282
----------------------
4383

4484
* MongoDB PHP extension min version raised up to 1.5.0. You should upgrade your environment in case you are
4585
using older version.
4686

47-
Upgrade from Yii 2.0.0
87+
Upgrade from 2.0.0
4888
----------------------
4989

5090
* MongoDB PHP extension min version raised up to 1.4.0. You should upgrade your environment in case you are

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@
1717
"email": "[email protected]"
1818
}
1919
],
20+
"minimum-stability": "dev",
2021
"require": {
21-
"yiisoft/yii2": "~2.0.14",
22+
"yiisoft/yii2": "~2.0.39",
2223
"ext-mongodb": ">=1.0.0"
2324
},
2425
"require-dev": {

src/Collection.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,17 @@ public function findOne($condition = [], $fields = [], $options = [])
241241
return empty($rows) ? null : current($rows);
242242
}
243243

244+
/**
245+
* Returns if a document exists.
246+
* @param array $condition Query condition.
247+
* @return bool
248+
* @since 2.0.39
249+
*/
250+
public function documentExists($condition = [])
251+
{
252+
return static::findOne($condition, ['_id' => 1]) !== null;
253+
}
254+
244255
/**
245256
* Updates a document and returns it.
246257
* @param array $condition query condition

src/Session.php

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,31 @@ public function init()
7474
$this->db = Instance::ensure($this->db, Connection::className());
7575
}
7676

77+
/**
78+
* Session open handler.
79+
* @internal Do not call this method directly.
80+
* @param string $savePath session save path
81+
* @param string $sessionName session name
82+
* @return bool whether session is opened successfully
83+
*/
84+
public function openSession($savePath, $sessionName)
85+
{
86+
if ($this->getUseStrictMode()) {
87+
$id = $this->getId();
88+
$collection = $this->db->getCollection($this->sessionCollection);
89+
$condition = [
90+
'id' => $id,
91+
'expire' => ['$gt' => time()],
92+
];
93+
if (!$collection->documentExists($condition)) {
94+
//This session id does not exist, mark it for forced regeneration
95+
$this->_forceRegenerateId = $id;
96+
}
97+
}
98+
99+
return parent::openSession($savePath, $sessionName);
100+
}
101+
77102
/**
78103
* Updates the current session ID with a newly generated one.
79104
* Please refer to <http://php.net/session_regenerate_id> for more details.
@@ -142,6 +167,11 @@ public function readSession($id)
142167
*/
143168
public function writeSession($id, $data)
144169
{
170+
if ($this->getUseStrictMode() && $id === $this->_forceRegenerateId) {
171+
//Ignore write when forceRegenerate is active for this id
172+
return true;
173+
}
174+
145175
// exception must be caught in session write handler
146176
// http://us.php.net/manual/en/function.session-set-save-handler.php
147177
try {
@@ -211,4 +241,4 @@ public function gcSession($maxLifetime)
211241

212242
return true;
213243
}
214-
}
244+
}

tests/CollectionTest.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,4 +560,22 @@ public function testDistinct()
560560
$this->assertFalse($rows === false);
561561
$this->assertCount(1, $rows);
562562
}
563+
564+
/**
565+
* @depends testInsert
566+
* @depends testFindOne
567+
*/
568+
public function testDocumentExists()
569+
{
570+
$name = uniqid('customer_', true);
571+
$collection = $this->getConnection()->getCollection('customer');
572+
$data = [
573+
'name' => $name,
574+
'address' => 'test address',
575+
];
576+
$collection->insert($data);
577+
578+
$this->assertTrue($collection->documentExists(['name' => $name]));
579+
$this->assertFalse($collection->documentExists(['name' => 'non-existing']));
580+
}
563581
}

tests/SessionTest.php

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use yii\mongodb\Session;
66
use Yii;
7+
use yii\helpers\ArrayHelper;
78

89
class SessionTest extends TestCase
910
{
@@ -22,13 +23,13 @@ protected function tearDown()
2223
* Creates test session instance.
2324
* @return Session session instance.
2425
*/
25-
protected function createSession()
26+
protected function createSession($config = [])
2627
{
27-
return Yii::createObject([
28+
return Yii::createObject(ArrayHelper::merge([
2829
'class' => Session::className(),
2930
'db' => $this->getConnection(),
3031
'sessionCollection' => static::$sessionCollection,
31-
]);
32+
], $config));
3233
}
3334

3435
// Tests:
@@ -157,4 +158,47 @@ public function testWriteCustomField()
157158
$this->assertEquals('session data', $rows[0]['data']);
158159
$this->assertEquals(15, $rows[0]['user_id']);
159160
}
160-
}
161+
162+
/**
163+
* @depends testWriteSession
164+
* @runInSeparateProcess
165+
*/
166+
public function testStrictMode()
167+
{
168+
//non-strict-mode test
169+
$nonStrictSession = $this->createSession([
170+
'useStrictMode' => false,
171+
]);
172+
$nonStrictSession->close();
173+
$nonStrictSession->destroySession('non-existing-non-strict');
174+
$nonStrictSession->setId('non-existing-non-strict');
175+
$nonStrictSession->open();
176+
$this->assertEquals('non-existing-non-strict', $nonStrictSession->getId());
177+
$nonStrictSession->close();
178+
179+
//strict-mode test
180+
$strictSession = $this->createSession([
181+
'useStrictMode' => true,
182+
]);
183+
$strictSession->close();
184+
$strictSession->destroySession('non-existing-strict');
185+
$strictSession->setId('non-existing-strict');
186+
$strictSession->open();
187+
$id = $strictSession->getId();
188+
$this->assertNotEquals('non-existing-strict', $id);
189+
$strictSession->set('strict_mode_test', 'session data');
190+
$strictSession->close();
191+
//Ensure session was not stored under forced id
192+
$strictSession->setId('non-existing-strict');
193+
$strictSession->open();
194+
$this->assertNotEquals('session data', $strictSession->get('strict_mode_test'));
195+
$strictSession->close();
196+
//Ensure session can be accessed with the new (and thus existing) id.
197+
$strictSession->setId($id);
198+
$strictSession->open();
199+
$this->assertNotEmpty($id);
200+
$this->assertEquals($id, $strictSession->getId());
201+
$this->assertEquals('session data', $strictSession->get('strict_mode_test'));
202+
$strictSession->close();
203+
}
204+
}

0 commit comments

Comments
 (0)