Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions _build/data/transport.core.context_settings.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<?php

use MODX\Revolution\modContextSetting;

$collection['0']= $xpdo->newObject(modContextSetting::class);
Expand All @@ -21,3 +22,13 @@
'area' => 'system',
'editedon' => NULL,
], '', true, true);
$collection['2'] = $xpdo->newObject(modContextSetting::class);
$collection['2']->fromArray([
'context_key' => 'mgr',
'key' => 'anonymous_sessions',
'value' => true,
'xtype' => 'combo-boolean',
'namespace' => 'core',
'area' => 'session',
'editedon' => null,
], '', true, true);
236 changes: 236 additions & 0 deletions _build/test/Tests/Model/Security/AnonymousSessionsContextTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
<?php

/*
* This file is part of the MODX Revolution package.
*
* Copyright (c) MODX, LLC
*
* For complete copyright and license information, see the COPYRIGHT and LICENSE
* files found in the top-level directory of this distribution.
*
* @package modx-test
*/

namespace MODX\Revolution\Tests\Model\Security;

use MODX\Revolution\modContext;
use MODX\Revolution\modContextSetting;
use MODX\Revolution\MODxTestCase;

/**
* Regression for #16424: mgr must keep anonymous_sessions enabled even when
* the system setting is disabled, so manager login can start a session without
* an existing cookie.
*
* @package modx-test
* @subpackage modx
* @group Model
* @group modX
* @group Session
*/
class AnonymousSessionsContextTest extends MODxTestCase
{
/** @var mixed */
private $originalSystemAnonymousSessions;

/** @var mixed */
private $originalMgrAnonymousSessions;

/** @var bool */
private $createdMgrSetting = false;

/** @var bool */
private $createdWebSetting = false;

/**
* @before
*/
public function setUpFixtures()
{
parent::setUpFixtures();

$this->originalSystemAnonymousSessions = $this->modx->getOption('anonymous_sessions');
$this->modx->setOption('anonymous_sessions', false);
$this->modx->_systemConfig['anonymous_sessions'] = false;

$mgrSetting = $this->modx->getObject(modContextSetting::class, [
'context_key' => modContext::CONTEXT_MANAGER,
'key' => 'anonymous_sessions',
]);
if (!$mgrSetting) {
$mgrSetting = $this->modx->newObject(modContextSetting::class);
$mgrSetting->fromArray([
'context_key' => modContext::CONTEXT_MANAGER,
'key' => 'anonymous_sessions',
'value' => true,
'xtype' => 'combo-boolean',
'namespace' => 'core',
'area' => 'session',
], '', true, true);
$this->assertTrue((bool)$mgrSetting->save(), 'Could not create mgr anonymous_sessions fixture.');
$this->createdMgrSetting = true;
$this->originalMgrAnonymousSessions = null;
} else {
$this->originalMgrAnonymousSessions = $mgrSetting->get('value');
$mgrSetting->set('value', true);
$this->assertTrue((bool)$mgrSetting->save(), 'Could not enable mgr anonymous_sessions fixture.');
}

$webSetting = $this->modx->getObject(modContextSetting::class, [
'context_key' => modContext::CONTEXT_DEFAULT,
'key' => 'anonymous_sessions',
]);
if ($webSetting) {
$webSetting->remove();
}
$webSetting = $this->modx->newObject(modContextSetting::class);
$webSetting->fromArray([
'context_key' => modContext::CONTEXT_DEFAULT,
'key' => 'anonymous_sessions',
'value' => false,
'xtype' => 'combo-boolean',
'namespace' => 'core',
'area' => 'session',
], '', true, true);
$this->assertTrue((bool)$webSetting->save(), 'Could not create web anonymous_sessions fixture.');
$this->createdWebSetting = true;

$this->refreshContextSettings();
}

/**
* @after
*/
public function tearDownFixtures()
{
if ($this->createdMgrSetting) {
$mgrSetting = $this->modx->getObject(modContextSetting::class, [
'context_key' => modContext::CONTEXT_MANAGER,
'key' => 'anonymous_sessions',
]);
if ($mgrSetting) {
$mgrSetting->remove();
}
} elseif ($this->originalMgrAnonymousSessions !== null) {
$mgrSetting = $this->modx->getObject(modContextSetting::class, [
'context_key' => modContext::CONTEXT_MANAGER,
'key' => 'anonymous_sessions',
]);
if ($mgrSetting) {
$mgrSetting->set('value', $this->originalMgrAnonymousSessions);
$mgrSetting->save();
}
}

if ($this->createdWebSetting) {
$webSetting = $this->modx->getObject(modContextSetting::class, [
'context_key' => modContext::CONTEXT_DEFAULT,
'key' => 'anonymous_sessions',
]);
if ($webSetting) {
$webSetting->remove();
}
}

$this->modx->setOption('anonymous_sessions', $this->originalSystemAnonymousSessions);
$this->modx->_systemConfig['anonymous_sessions'] = $this->originalSystemAnonymousSessions;
$this->refreshContextSettings();
$this->modx->reloadContext(modContext::CONTEXT_DEFAULT);

parent::tearDownFixtures();
}

public function testManagerContextOverridesSystemAnonymousSessionsFalse()
{
$this->assertTrue(
$this->activateContext(modContext::CONTEXT_MANAGER),
'Could not activate mgr context.'
);
$this->assertTrue(
$this->modx->paramValueIsTrue(
['anonymous_sessions' => $this->modx->getOption('anonymous_sessions')],
'anonymous_sessions'
),
'mgr must enable anonymous_sessions so login can start a session without a cookie.'
);
}

public function testWebContextCanDisableAnonymousSessions()
{
$this->assertTrue(
$this->activateContext(modContext::CONTEXT_MANAGER),
'Could not activate mgr context before returning to web.'
);
$this->assertTrue(
$this->activateContext(modContext::CONTEXT_DEFAULT),
'Could not activate web context.'
);
$this->assertFalse(
$this->modx->paramValueIsTrue(
['anonymous_sessions' => $this->modx->getOption('anonymous_sessions')],
'anonymous_sessions'
),
'web context should be able to disable anonymous_sessions independently of mgr.'
);
}

public function testUpgradeScriptEnsuresManagerAnonymousSessions()
{
$mgrSetting = $this->modx->getObject(modContextSetting::class, [
'context_key' => modContext::CONTEXT_MANAGER,
'key' => 'anonymous_sessions',
]);
if ($mgrSetting) {
$mgrSetting->remove();
}
$this->refreshContextSettings();

$modx = $this->modx;
$upgradeScript = dirname(__DIR__, 5) . '/setup/includes/upgrades/common/3.3.0-mgr-anonymous-sessions.php';
include $upgradeScript;

$mgrSetting = $this->modx->getObject(modContextSetting::class, [
'context_key' => modContext::CONTEXT_MANAGER,
'key' => 'anonymous_sessions',
]);
$this->assertInstanceOf(modContextSetting::class, $mgrSetting);
$this->assertTrue(
$this->modx->paramValueIsTrue(['value' => $mgrSetting->get('value')], 'value'),
'Upgrade must create mgr anonymous_sessions=Yes.'
);

$mgrSetting->set('value', false);
$this->assertTrue((bool)$mgrSetting->save());

include $upgradeScript;

$mgrSetting = $this->modx->getObject(modContextSetting::class, [
'context_key' => modContext::CONTEXT_MANAGER,
'key' => 'anonymous_sessions',
]);
$this->assertTrue(
$this->modx->paramValueIsTrue(['value' => $mgrSetting->get('value')], 'value'),
'Upgrade must restore mgr anonymous_sessions=Yes if it was disabled.'
);
$this->createdMgrSetting = true;
}

private function activateContext(string $contextKey): bool
{
if ($this->modx->context instanceof modContext && $this->modx->context->get('key') === $contextKey) {
return (bool)$this->modx->reloadContext($contextKey);
}

return (bool)$this->modx->switchContext($contextKey, true);
}

private function refreshContextSettings(): void
{
$this->modx->cacheManager->refresh([
'context_settings' => [
'contexts' => [modContext::CONTEXT_MANAGER, modContext::CONTEXT_DEFAULT],
],
]);
unset($this->modx->contexts[modContext::CONTEXT_MANAGER], $this->modx->contexts[modContext::CONTEXT_DEFAULT]);
}
}
2 changes: 1 addition & 1 deletion core/lexicon/en/setting.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@
$_lang['setting_allow_tv_eval_desc'] = 'Select this option to enable or disable eval in TV bindings. If this option is set to no, the code/value will just be handled as regular text.';

$_lang['setting_anonymous_sessions'] = 'Anonymous Sessions';
$_lang['setting_anonymous_sessions_desc'] = 'If disabled, only authenticated users will have access to a PHP session. This can reduce overhead for anonymous users and the load they impose on a MODX site if they do not need access to a unique session. If session_enabled is false, this setting has no effect as sessions would never be available.';
$_lang['setting_anonymous_sessions_desc'] = 'When authentication or other persistence features that require a PHP Session are not needed, set this to “No.” The Manager (mgr) Context must set this to “Yes” to operate correctly. Note that in multi-Context setups, the system-wide value for this setting can be overridden at the Context level.';

$_lang['setting_archive_with'] = 'Force PCLZip Archives';
$_lang['setting_archive_with_desc'] = 'If true, will use PCLZip instead of ZipArchive as the zip extension. Turn this on if you are getting extractTo errors or are having problems with unzipping in Package Management.';
Expand Down
43 changes: 43 additions & 0 deletions setup/includes/upgrades/common/3.3.0-mgr-anonymous-sessions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

/**
* Ensure the manager context always starts PHP sessions.
*
* Fixes #16424: with system anonymous_sessions=No and no session cookie,
* manager login could not start a session. Keep anonymous_sessions enabled
* on the mgr context so public contexts can still disable it.
*
* @var modX $modx
* @package setup
*/

use MODX\Revolution\modContext;
use MODX\Revolution\modContextSetting;

$criteria = [
'context_key' => modContext::CONTEXT_MANAGER,
'key' => 'anonymous_sessions',
];

$setting = $modx->getObject(modContextSetting::class, $criteria);
if (!$setting) {
$setting = $modx->newObject(modContextSetting::class);
$setting->fromArray([
'context_key' => modContext::CONTEXT_MANAGER,
'key' => 'anonymous_sessions',
'value' => true,
'xtype' => 'combo-boolean',
'namespace' => 'core',
'area' => 'session',
], '', true, true);
$setting->save();
} elseif (!$modx->paramValueIsTrue(['value' => $setting->get('value')], 'value')) {
$setting->set('value', true);
$setting->save();
}

if ($modx->cacheManager) {
$modx->cacheManager->refresh([
'context_settings' => ['contexts' => [modContext::CONTEXT_MANAGER]],
]);
}
12 changes: 12 additions & 0 deletions setup/includes/upgrades/mysql/3.3.0-pl.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

/**
* Specific upgrades for Revolution 3.3.0-pl
*
* @var modX $modx
* @var modInstallVersion $this
* @package setup
* @subpackage upgrades
*/

include dirname(__DIR__) . '/common/3.3.0-mgr-anonymous-sessions.php';
Loading