[TASK] Add functional tests for dynamic values in Extbase query cache 63/29963/9
authorMarc Bastian Heinrichs <typo3@mbh-software.de>
Thu, 8 May 2014 01:31:21 +0000 (03:31 +0200)
committerChristian Kuhn <lolli@schwarzbu.ch>
Mon, 2 Feb 2015 21:17:54 +0000 (22:17 +0100)
For performance reasons Extbase caches database queries. To ensure the
security relevant aspect, the frontend usergroup has to be parameterized
and be added by runtime. This patch adds functional tests to ensure
query caching by:

* adding possibility to simulate a frontend user in functional tests
* extending blog_example extension TCA adding fe_group for blogs
* adding a plugin and controller for blog listing
* adding new fixtures with frontend usergroups restrictions
* adding functional tests and frontend rending function

Resolves: #58655
Releases: master
Change-Id: Ib6d918461d5ad04bc2236a375f558d48548c9946
Reviewed-on: http://review.typo3.org/29963
Reviewed-by: Wouter Wolters <typo3@wouterwolters.nl>
Tested-by: Wouter Wolters <typo3@wouterwolters.nl>
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
13 files changed:
typo3/sysext/core/Tests/Functional/Fixtures/Frontend/AdditionalConfiguration.php
typo3/sysext/core/Tests/Functional/Framework/Frontend/Hook/FrontendUserHandler.php [new file with mode: 0644]
typo3/sysext/core/Tests/FunctionalTestCase.php
typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/Classes/Controller/BlogController.php [new file with mode: 0644]
typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/Configuration/TCA/Blog.php
typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/ext_localconf.php [new file with mode: 0644]
typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/ext_tables.php
typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/ext_tables.sql
typo3/sysext/extbase/Tests/Functional/Persistence/EnableFieldsTest.php [new file with mode: 0644]
typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/Frontend/JsonRenderer.ts [new file with mode: 0644]
typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/blogs-with-fe_groups.xml [new file with mode: 0644]
typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/fe_groups.xml [new file with mode: 0644]
typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/fe_users.xml [new file with mode: 0644]

index 25a7a77..c6975f8 100644 (file)
@@ -11,6 +11,8 @@ $GLOBALS['TYPO3_CONF_VARS']['FE']['debug'] = FALSE;
 
 // Register hooks for frontend test
 if (TYPO3_MODE === 'FE') {
+       $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['initFEuser']['FunctionalTest'] =
+               \TYPO3\CMS\Core\Tests\Functional\Framework\Frontend\Hook\FrontendUserHandler::class . '->initialize';
        $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/index_ts.php']['postBeUser']['FunctionalTest'] =
                \TYPO3\CMS\Core\Tests\Functional\Framework\Frontend\Hook\BackendUserHandler::class . '->initialize';
 }
diff --git a/typo3/sysext/core/Tests/Functional/Framework/Frontend/Hook/FrontendUserHandler.php b/typo3/sysext/core/Tests/Functional/Framework/Frontend/Hook/FrontendUserHandler.php
new file mode 100644 (file)
index 0000000..4a4f901
--- /dev/null
@@ -0,0 +1,50 @@
+<?php
+namespace TYPO3\CMS\Core\Tests\Functional\Framework\Frontend\Hook;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+
+/**
+ * Handler for frontend user
+ */
+class FrontendUserHandler implements \TYPO3\CMS\Core\SingletonInterface {
+
+       /**
+        * Initialize
+        *
+        * @param array $parameters
+        * @param \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController $frontendController
+        */
+       public function initialize(array $parameters, \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController $frontendController) {
+               $frontendUserId = (int)GeneralUtility::_GP('frontendUserId');
+               $frontendController->fe_user->checkPid = 0;
+
+               $frontendUser = $this->getDatabaseConnection()->exec_SELECTgetSingleRow('*', 'fe_users', 'uid =' . $frontendUserId);
+               if (is_array($frontendUser)) {
+                       $frontendController->loginUser = 1;
+                       $frontendController->fe_user->createUserSession($frontendUser);
+                       $frontendController->fe_user->user = $GLOBALS['TSFE']->fe_user->fetchUserSession();
+                       $frontendController->initUserGroups();
+               }
+       }
+
+       /**
+        * @return \TYPO3\CMS\Core\Database\DatabaseConnection
+        */
+       protected function getDatabaseConnection() {
+               return $GLOBALS['TYPO3_DB'];
+       }
+
+}
index fa7735c..d1e89bf 100644 (file)
@@ -329,13 +329,18 @@ abstract class FunctionalTestCase extends BaseTestCase {
         * @param int $backendUserId
         * @param int $workspaceId
         * @param bool $failOnFailure
+        * @param int $frontendUserId
         * @return Response
         */
-       protected function getFrontendResponse($pageId, $languageId = 0, $backendUserId = 0, $workspaceId = 0, $failOnFailure = TRUE) {
+       protected function getFrontendResponse($pageId, $languageId = 0, $backendUserId = 0, $workspaceId = 0, $failOnFailure = TRUE, $frontendUserId = 0) {
                $pageId = (int)$pageId;
                $languageId = (int)$languageId;
 
                $additionalParameter = '';
+
+               if (!empty($frontendUserId)) {
+                       $additionalParameter .= '&frontendUserId=' . (int)$frontendUserId;
+               }
                if (!empty($backendUserId)) {
                        $additionalParameter .= '&backendUserId=' . (int)$backendUserId;
                }
diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/Classes/Controller/BlogController.php b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/Classes/Controller/BlogController.php
new file mode 100644 (file)
index 0000000..8158c25
--- /dev/null
@@ -0,0 +1,111 @@
+<?php
+
+namespace ExtbaseTeam\BlogExample\Controller;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+/**
+ * BlogController
+ */
+class BlogController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionController {
+
+       /**
+        * @inject
+        * @var \ExtbaseTeam\BlogExample\Domain\Repository\BlogRepository
+        */
+       protected $blogRepository;
+
+       /**
+        * @var string
+        */
+       protected $defaultViewObjectName = \TYPO3\CMS\Extbase\Mvc\View\JsonView::class;
+
+       /**
+        * @inject
+        * @var \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapFactory
+        */
+       protected $dataMapFactory;
+
+       /**
+        * @return array
+        */
+       public function listAction() {
+               $blogs = $this->blogRepository->findAll();
+               $value[$this->getRuntimeIdentifier()] = $this->getStructure($blogs);
+
+               $this->view->assign('value', $value);
+       }
+
+       /**
+        * @param \TYPO3\CMS\Extbase\Mvc\RequestInterface $request
+        * @param \TYPO3\CMS\Extbase\Mvc\ResponseInterface $response
+        * @throws \RuntimeException
+        */
+       public function processRequest(\TYPO3\CMS\Extbase\Mvc\RequestInterface $request, \TYPO3\CMS\Extbase\Mvc\ResponseInterface $response) {
+               try {
+                       parent::processRequest($request, $response);
+               } catch (\TYPO3\CMS\Extbase\Property\Exception $exception) {
+                       throw new \RuntimeException(
+                               $this->getRuntimeIdentifier() . ': ' . $exception->getMessage() . ' (' . $exception->getCode() . ')'
+                       );
+               }
+       }
+
+       /**
+        * @param \Iterator|\TYPO3\CMS\Extbase\DomainObject\AbstractEntity[] $iterator
+        * @return array
+        */
+       protected function getStructure($iterator) {
+               $structure = array();
+
+               if (!$iterator instanceof \Iterator) {
+                       $iterator = array($iterator);
+               }
+
+               foreach ($iterator as $entity) {
+                       $dataMap = $this->dataMapFactory->buildDataMap(get_class($entity));
+                       $tableName = $dataMap->getTableName();
+                       $identifier = $tableName . ':' . $entity->getUid();
+                       $properties = \TYPO3\CMS\Extbase\Reflection\ObjectAccess::getGettableProperties($entity);
+
+                       $structureItem = array();
+                       foreach ($properties as $propertyName => $propertyValue) {
+                               $columnMap = $dataMap->getColumnMap($propertyName);
+                               if ($columnMap !== NULL) {
+                                       $propertyName = $columnMap->getColumnName();
+                               }
+                               if ($propertyValue instanceof \Iterator) {
+                                       $structureItem[$propertyName] = $this->getStructure($propertyValue);
+                               } else {
+                                       $structureItem[$propertyName] = $propertyValue;
+                               }
+                       }
+                       $structure[$identifier] = $structureItem;
+               }
+
+               return $structure;
+       }
+
+       /**
+        * @return string
+        */
+       protected function getRuntimeIdentifier() {
+               $arguments = array();
+               foreach ($this->request->getArguments() as $argumentName => $argumentValue) {
+                       $arguments[] = $argumentName . '=' . $argumentValue;
+               }
+               return $this->request->getControllerActionName() . '(' . implode(', ', $arguments) . ')';
+       }
+
+}
\ No newline at end of file
index 79f2ab2..f851efe 100644 (file)
@@ -53,6 +53,32 @@ $TCA['tx_blogexample_domain_model_blog'] = array(
                                'type' => 'check'
                        )
                ),
+               'fe_group' => array(
+                       'exclude' => 1,
+                       'label' => 'LLL:EXT:lang/locallang_general.xlf:LGL.fe_group',
+                       'config' => array(
+                               'type' => 'select',
+                               'size' => 5,
+                               'maxitems' => 20,
+                               'items' => array(
+                                       array(
+                                               'LLL:EXT:lang/locallang_general.xlf:LGL.hide_at_login',
+                                               -1,
+                                       ),
+                                       array(
+                                               'LLL:EXT:lang/locallang_general.xlf:LGL.any_login',
+                                               -2,
+                                       ),
+                                       array(
+                                               'LLL:EXT:lang/locallang_general.xlf:LGL.usergroups',
+                                               '--div--',
+                                       ),
+                               ),
+                               'exclusiveKeys' => '-1,-2',
+                               'foreign_table' => 'fe_groups',
+                               'foreign_table_where' => 'ORDER BY fe_groups.title',
+                       ),
+               ),
                'title' => array(
                        'exclude' => 0,
                        'label' => 'LLL:EXT:blog_example/Resources/Private/Language/locallang_db.xml:tx_blogexample_domain_model_blog.title',
@@ -139,7 +165,7 @@ $TCA['tx_blogexample_domain_model_blog'] = array(
                ),
        ),
        'types' => array(
-               '1' => array('showitem' => 'sys_language_uid, hidden, title, description, logo, posts, administrator')
+               '1' => array('showitem' => 'sys_language_uid, hidden, fe_group, title, description, logo, posts, administrator')
        ),
        'palettes' => array(
                '1' => array('showitem' => '')
diff --git a/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/ext_localconf.php b/typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example/ext_localconf.php
new file mode 100644 (file)
index 0000000..b4a6a66
--- /dev/null
@@ -0,0 +1,10 @@
+<?php
+defined('TYPO3_MODE') or die();
+
+\TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin(
+       'ExtbaseTeam.' . $_EXTKEY, 'Blogs',
+       array(
+               'Blog' => 'list',
+       ),
+       array()
+);
index 216b7ad..3504208 100644 (file)
@@ -9,6 +9,8 @@ defined('TYPO3_MODE') or die();
 \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addStaticFile($_EXTKEY, 'Configuration/TypoScript', 'BlogExample setup');
 \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addStaticFile($_EXTKEY, 'Configuration/TypoScript/DefaultStyles', 'BlogExample CSS Styles (optional)');
 
+\TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerPlugin($_EXTKEY, 'Blogs', 'Blog listing');
+
 \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::allowTableOnStandardPages('tx_blogexample_domain_model_blog');
 $TCA['tx_blogexample_domain_model_blog'] = array (
        'ctrl' => array (
@@ -24,8 +26,9 @@ $TCA['tx_blogexample_domain_model_blog'] = array (
                'transOrigDiffSourceField' => 'l18n_diffsource',
                'delete' => 'deleted',
                'enablecolumns' => array(
-                       'disabled' => 'hidden'
-                       ),
+                       'disabled' => 'hidden',
+                       'fe_group' => 'fe_group',
+               ),
                'dynamicConfigFile' => \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath($_EXTKEY) . 'Configuration/TCA/Blog.php',
                'iconfile' => \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extRelPath($_EXTKEY) . 'Resources/Public/Icons/icon_tx_blogexample_domain_model_blog.gif'
        )
index 54c183e..5e3745f 100644 (file)
@@ -16,6 +16,7 @@ CREATE TABLE tx_blogexample_domain_model_blog (
        crdate int(11) unsigned DEFAULT '0' NOT NULL,
        deleted tinyint(4) unsigned DEFAULT '0' NOT NULL,
        hidden tinyint(4) unsigned DEFAULT '0' NOT NULL,
+       fe_group varchar(100) DEFAULT '0' NOT NULL,
 
        t3ver_oid int(11) DEFAULT '0' NOT NULL,
        t3ver_id int(11) DEFAULT '0' NOT NULL,
diff --git a/typo3/sysext/extbase/Tests/Functional/Persistence/EnableFieldsTest.php b/typo3/sysext/extbase/Tests/Functional/Persistence/EnableFieldsTest.php
new file mode 100644 (file)
index 0000000..199f09b
--- /dev/null
@@ -0,0 +1,94 @@
+<?php
+namespace TYPO3\CMS\Extbase\Tests\Functional\Persistence;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+require_once __DIR__ . '/../../../../core/Tests/Functional/DataHandling/AbstractDataHandlerActionTestCase.php';
+
+use TYPO3\CMS\Core\Tests\Functional\DataHandling\AbstractDataHandlerActionTestCase;
+
+/**
+ * Enable fields test
+ */
+class EnableFieldsTest extends AbstractDataHandlerActionTestCase {
+
+       const TABLE_Blog = 'tx_blogexample_domain_model_blog';
+
+       /**
+        * @var array
+        */
+       protected $testExtensionsToLoad = array('typo3/sysext/extbase/Tests/Functional/Fixtures/Extensions/blog_example');
+
+       /**
+        * @var array
+        */
+       protected $coreExtensionsToLoad = array('sv', 'extbase', 'fluid');
+
+       /**
+        * Sets up this test suite.
+        */
+       public function setUp() {
+               parent::setUp();
+
+               $this->importDataSet(ORIGINAL_ROOT . 'typo3/sysext/core/Tests/Functional/Fixtures/pages.xml');
+               $this->importDataSet(ORIGINAL_ROOT . 'typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/fe_groups.xml');
+               $this->importDataSet(ORIGINAL_ROOT . 'typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/fe_users.xml');
+               $this->importDataSet(ORIGINAL_ROOT . 'typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/blogs-with-fe_groups.xml');
+
+               $this->setUpFrontendRootPage(1, array('typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/Frontend/JsonRenderer.ts'));
+       }
+
+       /**
+        * @test
+        */
+       public function protectedRecordsNotFoundIfNoUserLoggedIn() {
+               $responseSections = $this->getFrontendResponse(1)->getResponseSections('Extbase:list()');
+               $this->assertThat($responseSections, $this->getRequestSectionHasRecordConstraint()
+                       ->setTable(self::TABLE_Blog)->setField('title')->setValues('Blog1'));
+       }
+
+       /**
+        * @test
+        */
+       public function onlyReturnProtectedRecordsForTheFirstUserGroup() {
+               $responseSections = $this->getFrontendResponse(1, 0, 0, 0, TRUE, 1)->getResponseSections('Extbase:list()');
+               $this->assertThat($responseSections, $this->getRequestSectionHasRecordConstraint()
+                       ->setTable(self::TABLE_Blog)->setField('title')->setValues('Blog1', 'Blog2'));
+       }
+
+       /**
+        * @test
+        */
+       public function onlyReturnProtectedRecordsForTheSecondUserGroup() {
+               $responseSections = $this->getFrontendResponse(1, 0, 0, 0, TRUE, 2)->getResponseSections('Extbase:list()');
+               $this->assertThat($responseSections, $this->getRequestSectionHasRecordConstraint()
+                       ->setTable(self::TABLE_Blog)->setField('title')->setValues('Blog1', 'Blog3'));
+       }
+
+       /**
+        * @test
+        */
+       public function onlyOwnProtectedRecordsWithQueryCacheInvolvedAreReturned() {
+               // first request to fill the query cache
+               $responseSections = $this->getFrontendResponse(1, 0, 0, 0, TRUE, 1)->getResponseSections('Extbase:list()');
+               $this->assertThat($responseSections, $this->getRequestSectionHasRecordConstraint()
+                       ->setTable(self::TABLE_Blog)->setField('title')->setValues('Blog1', 'Blog2'));
+
+               // second request with other frontenduser
+               $responseSections = $this->getFrontendResponse(1, 0, 0, 0, TRUE, 2)->getResponseSections('Extbase:list()');
+               $this->assertThat($responseSections, $this->getRequestSectionHasRecordConstraint()
+                       ->setTable(self::TABLE_Blog)->setField('title')->setValues('Blog1', 'Blog3'));
+       }
+
+}
diff --git a/typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/Frontend/JsonRenderer.ts b/typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/Frontend/JsonRenderer.ts
new file mode 100644 (file)
index 0000000..2b01131
--- /dev/null
@@ -0,0 +1,40 @@
+config {
+       no_cache = 1
+       debug = 0
+       xhtml_cleaning = 0
+       admPanel = 0
+       disableAllHeaderCode = 1
+       sendCacheHeaders = 0
+       sys_language_uid = 0
+       sys_language_mode = ignore
+       sys_language_overlay = 1
+       additionalHeaders = Content-Type: application/json; charset=utf-8
+}
+
+plugin.tx_blogexample {
+       persistence {
+               storagePid = 1
+       }
+}
+
+page = PAGE
+page {
+       10 = COA
+       10 {
+               10 = USER
+               10 {
+                       userFunc = TYPO3\CMS\Extbase\Core\Bootstrap->run
+                       extensionName = BlogExample
+                       pluginName = Blogs
+                       vendorName = ExtbaseTeam
+               }
+               stdWrap.postUserFunc = TYPO3\CMS\Core\Tests\Functional\Framework\Frontend\Renderer->parseValues
+               stdWrap.postUserFunc.as = Extbase
+       }
+
+       stdWrap.postUserFunc = TYPO3\CMS\Core\Tests\Functional\Framework\Frontend\Renderer->renderSections
+}
+
+[globalVar = GP:L = 1]
+       config.sys_language_uid = 1
+[end]
diff --git a/typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/blogs-with-fe_groups.xml b/typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/blogs-with-fe_groups.xml
new file mode 100644 (file)
index 0000000..70b8194
--- /dev/null
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dataset>
+       <tx_blogexample_domain_model_blog>
+               <uid>1</uid>
+               <pid>1</pid>
+               <title>Blog1</title>
+               <deleted>0</deleted>
+               <posts>0</posts>
+       </tx_blogexample_domain_model_blog>
+       <tx_blogexample_domain_model_blog>
+               <uid>2</uid>
+               <pid>1</pid>
+               <title>Blog2</title>
+               <fe_group>1</fe_group>
+               <deleted>0</deleted>
+               <posts>0</posts>
+       </tx_blogexample_domain_model_blog>
+       <tx_blogexample_domain_model_blog>
+               <uid>3</uid>
+               <pid>1</pid>
+               <title>Blog3</title>
+               <fe_group>2</fe_group>
+               <deleted>0</deleted>
+               <posts>0</posts>
+       </tx_blogexample_domain_model_blog>
+</dataset>
diff --git a/typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/fe_groups.xml b/typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/fe_groups.xml
new file mode 100644 (file)
index 0000000..ff91af4
--- /dev/null
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dataset>
+       <fe_groups>
+               <uid>1</uid>
+               <pid>1</pid>
+               <title>Group A</title>
+               <deleted>0</deleted>
+       </fe_groups>
+       <fe_groups>
+               <uid>2</uid>
+               <pid>1</pid>
+               <title>Group B</title>
+               <deleted>0</deleted>
+       </fe_groups>
+</dataset>
diff --git a/typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/fe_users.xml b/typo3/sysext/extbase/Tests/Functional/Persistence/Fixtures/fe_users.xml
new file mode 100644 (file)
index 0000000..4aad90d
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dataset>
+       <fe_users>
+               <uid>1</uid>
+               <pid>0</pid>
+               <username>usera</username>
+               <deleted>0</deleted>
+               <usergroup>1</usergroup>
+               <disable>0</disable>
+       </fe_users>
+       <fe_users>
+               <uid>2</uid>
+               <pid>0</pid>
+               <username>userb</username>
+               <deleted>0</deleted>
+               <usergroup>2</usergroup>
+               <disable>0</disable>
+       </fe_users>
+</dataset>