[FEATURE] (MVC): Whitelist-based Property Mapping Configuration
authorSebastian Kurfürst <sebastian@typo3.org>
Sat, 9 Feb 2013 21:04:35 +0000 (22:04 +0100)
committerAnja Leichsenring <aleichsenring@ab-softlab.de>
Sat, 9 Feb 2013 21:14:02 +0000 (22:14 +0100)
Up to now, property mapping in the rewritten property mapper always
allowed to modify all properties of a given object. Especially in the
MVC stack, this functionality was relied upon for all update and create
actions. However, for nested objects, the user needed to configure
whether updates and creations should be allowed.
This was an inconsistent behavior, especially because for read-only
actions the object could be also modified.
The behavior is now changed to be more predictive:
- the default PropertyMappingConfiguration used in the MVC stack is
  changed to be very restrictive: we do neither allow creation of any
  new objects nor modification of existing ones; and all properties
  which should be modified must be explicitly configured.
- For each form, Fluid now generates a list of trusted properties,
  based upon which the PropertyMappingConfiguration is set correctly.
  This means only properties which have been rendered by fluid are
  allowed to be modified, and creation / insertion is only permitted
  if needed.

(Excerpt from commit message of the original change in Flow)
https://review.typo3.org/#/c/10926/

This Patch is a 100% backport from this change with some needed
adjustments for the unit tests.

Change-Id: I6e59183a6ddfe83c65f975a2e2211e9ac33c5ccc
Resolves: #43057
Releases: 6.1
Reviewed-on: https://review.typo3.org/17092
Reviewed-by: Marc Bastian Heinrichs
Tested-by: Marc Bastian Heinrichs
Reviewed-by: Helmut Hummel
Tested-by: Helmut Hummel
Reviewed-by: Anja Leichsenring
Tested-by: Anja Leichsenring
typo3/sysext/extbase/Classes/Mvc/Controller/ActionController.php
typo3/sysext/extbase/Classes/Mvc/Controller/Argument.php
typo3/sysext/extbase/Classes/Mvc/Controller/MvcPropertyMappingConfigurationService.php [new file with mode: 0644]
typo3/sysext/extbase/Classes/Property/PropertyMapper.php
typo3/sysext/extbase/Classes/Property/PropertyMappingConfiguration.php
typo3/sysext/extbase/Classes/Property/PropertyMappingConfigurationBuilder.php
typo3/sysext/extbase/Tests/Unit/Mvc/Controller/ActionControllerTest.php
typo3/sysext/extbase/Tests/Unit/Mvc/Controller/ArgumentTest.php
typo3/sysext/extbase/Tests/Unit/Mvc/Controller/MvcPropertyMappingConfigurationServiceTest.php [new file with mode: 0644]
typo3/sysext/extbase/Tests/Unit/Property/PropertyMappingConfigurationTest.php

index 0bc4efa..a200797 100644 (file)
@@ -98,6 +98,12 @@ class ActionController extends \TYPO3\CMS\Extbase\Mvc\Controller\AbstractControl
        protected $errorMethodName = 'errorAction';
 
        /**
+        * @var \TYPO3\CMS\Extbase\Mvc\Controller\MvcPropertyMappingConfigurationService
+        * @api
+        */
+       protected $mvcPropertyMappingConfigurationService;
+
+       /**
         * @param \TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService
         * @return void
         */
@@ -114,6 +120,13 @@ class ActionController extends \TYPO3\CMS\Extbase\Mvc\Controller\AbstractControl
        }
 
        /**
+        * @param \TYPO3\CMS\Extbase\Mvc\Controller\MvcPropertyMappingConfigurationService $mvcPropertyMappingConfigurationService
+        */
+       public function injectMvcPropertyMappingConfigurationService(\TYPO3\CMS\Extbase\Mvc\Controller\MvcPropertyMappingConfigurationService $mvcPropertyMappingConfigurationService) {
+               $this->mvcPropertyMappingConfigurationService = $mvcPropertyMappingConfigurationService;
+       }
+
+       /**
         * Checks if the current request type is supported by the controller.
         *
         * If your controller only supports certain request types, either
@@ -150,6 +163,7 @@ class ActionController extends \TYPO3\CMS\Extbase\Mvc\Controller\AbstractControl
                $this->actionMethodName = $this->resolveActionMethodName();
                $this->initializeActionMethodArguments();
                $this->initializeActionMethodValidators();
+               $this->mvcPropertyMappingConfigurationService->initializePropertyMappingConfigurationFromRequest($request, $this->arguments);
                $this->initializeAction();
                $actionInitializationMethodName = 'initialize' . ucfirst($this->actionMethodName);
                if (method_exists($this, $actionInitializationMethodName)) {
index 1d72e36..f9d5cad 100644 (file)
@@ -64,11 +64,6 @@ class Argument {
        protected $propertyMapper;
 
        /**
-        * @var \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationBuilder
-        */
-       protected $propertyMappingConfigurationBuilder;
-
-       /**
         * @var \TYPO3\CMS\Extbase\Mvc\Controller\MvcPropertyMappingConfiguration
         */
        protected $propertyMappingConfiguration;
@@ -218,14 +213,6 @@ class Argument {
        }
 
        /**
-        * @param \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationBuilder $propertyMappingConfigurationBuilder
-        * @return void
-        */
-       public function injectPropertyMappingConfigurationBuilder(\TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationBuilder $propertyMappingConfigurationBuilder) {
-               $this->propertyMappingConfigurationBuilder = $propertyMappingConfigurationBuilder;
-       }
-
-       /**
         * @param \TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService
         * @return void
         */
@@ -264,21 +251,20 @@ class Argument {
        }
 
        /**
-        * Initializer.
-        *
+        * @param \TYPO3\CMS\Extbase\Service\TypeHandlingService $typeHandlingService
         * @return void
         */
-       public function initializeObject() {
-               $this->propertyMappingConfiguration = $this->propertyMappingConfigurationBuilder->build('TYPO3\\CMS\\Extbase\\Mvc\\Controller\\MvcPropertyMappingConfiguration');
+       public function injectTypeHandlingService(\TYPO3\CMS\Extbase\Service\TypeHandlingService $typeHandlingService) {
+               $this->typeHandlingService = $typeHandlingService;
+               $this->dataType = $this->typeHandlingService->normalizeType($this->dataType);
        }
 
        /**
-        * @param \TYPO3\CMS\Extbase\Service\TypeHandlingService $typeHandlingService
+        * @param \TYPO3\CMS\Extbase\Mvc\Controller\MvcPropertyMappingConfiguration $mvcPropertyMappingConfiguration
         * @return void
         */
-       public function injectTypeHandlingService(\TYPO3\CMS\Extbase\Service\TypeHandlingService $typeHandlingService) {
-               $this->typeHandlingService = $typeHandlingService;
-               $this->dataType = $this->typeHandlingService->normalizeType($this->dataType);
+       public function injectPropertyMappingConfiguration(\TYPO3\CMS\Extbase\Mvc\Controller\MvcPropertyMappingConfiguration $mvcPropertyMappingConfiguration) {
+               $this->propertyMappingConfiguration = $mvcPropertyMappingConfiguration;
        }
 
        /**
diff --git a/typo3/sysext/extbase/Classes/Mvc/Controller/MvcPropertyMappingConfigurationService.php b/typo3/sysext/extbase/Classes/Mvc/Controller/MvcPropertyMappingConfigurationService.php
new file mode 100644 (file)
index 0000000..5adfe19
--- /dev/null
@@ -0,0 +1,183 @@
+<?php
+namespace TYPO3\CMS\Extbase\Mvc\Controller;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2010-2012 Extbase Team (http://forge.typo3.org/projects/typo3v4-mvc)
+ *  Extbase is a backport of TYPO3 Flow. All credits go to the TYPO3 Flow team.
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *  A copy is found in the textfile GPL.txt and important notices to the license
+ *  from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+/**
+ * This is a Service which can generate a request hash and check whether the currently given arguments
+ * fit to the request hash.
+ *
+ * It is used when forms are generated and submitted:
+ * After a form has been generated, the method "generateRequestHash" is called with the names of all form fields.
+ * It cleans up the array of form fields and creates another representation of it, which is then serialized and hashed.
+ *
+ * Both serialized form field list and the added hash form the request hash, which will be sent over the wire (as an argument __hmac).
+ *
+ * On the validation side, the validation happens in two steps:
+ * 1) Check if the request hash is consistent (the hash value fits to the serialized string)
+ * 2) Check that _all_ GET/POST parameters submitted occur inside the form field list of the request hash.
+ *
+ * Note: It is crucially important that a private key is computed into the hash value! This is done inside the HashService.
+ */
+class MvcPropertyMappingConfigurationService implements \TYPO3\CMS\Core\SingletonInterface {
+
+       /**
+        * The hash service class to use
+        *
+        * @var \TYPO3\CMS\Extbase\Security\Cryptography\HashService
+        */
+       protected $hashService;
+
+       /**
+        * Inject the hash service
+        *
+        * @param \TYPO3\CMS\Extbase\Security\Cryptography\HashService $hashService
+        */
+       public function injectHashService(\TYPO3\CMS\Extbase\Security\Cryptography\HashService $hashService) {
+               $this->hashService = $hashService;
+       }
+
+       /**
+        * Generate a request hash for a list of form fields
+        *
+        * @param array $formFieldNames Array of form fields
+        * @param string $fieldNamePrefix
+        *
+        * @return string trusted properties token
+        * @throws \TYPO3\CMS\EXTBASE\Security\Exception\InvalidArgumentForHashGenerationException
+        */
+       public function generateTrustedPropertiesToken($formFieldNames, $fieldNamePrefix = '') {
+               $formFieldArray = array();
+               foreach ($formFieldNames as $formField) {
+                       $formFieldParts = explode('[', $formField);
+                       $currentPosition = &$formFieldArray;
+                       for ($i = 0; $i < count($formFieldParts); $i++) {
+                               $formFieldPart = $formFieldParts[$i];
+                               $formFieldPart = rtrim($formFieldPart, ']');
+                               if (!is_array($currentPosition)) {
+                                       throw new \TYPO3\CMS\Extbase\Security\Exception\InvalidArgumentForHashGenerationException('The form field "' . $formField . '" is declared as array, but it collides with a previous form field of the same name which declared the field as string. This is an inconsistency you need to fix inside your Fluid form. (String overridden by Array)', 1255072196);
+                               }
+                               if ($i === count($formFieldParts) - 1) {
+                                       if (isset($currentPosition[$formFieldPart]) && is_array($currentPosition[$formFieldPart])) {
+                                               throw new \TYPO3\CMS\Extbase\Security\Exception\InvalidArgumentForHashGenerationException('The form field "' . $formField . '" is declared as string, but it collides with a previous form field of the same name which declared the field as array. This is an inconsistency you need to fix inside your Fluid form. (Array overridden by String)', 1255072587);
+                                       }
+                                       // Last iteration - add a string
+                                       if ($formFieldPart === '') {
+                                               $currentPosition[] = 1;
+                                       } else {
+                                               $currentPosition[$formFieldPart] = 1;
+                                       }
+                               } else {
+                                       if ($formFieldPart === '') {
+                                               throw new \TYPO3\CMS\Extbase\Security\Exception\InvalidArgumentForHashGenerationException('The form field "' . $formField . '" is invalid. Reason: "[]" used not as last argument, but somewhere in the middle (like foo[][bar]).', 1255072832);
+                                       }
+                                       if (!isset($currentPosition[$formFieldPart])) {
+                                               $currentPosition[$formFieldPart] = array();
+                                       }
+                                       $currentPosition = &$currentPosition[$formFieldPart];
+                               }
+                       }
+               }
+               if ($fieldNamePrefix !== '') {
+                       $formFieldArray = (isset($formFieldArray[$fieldNamePrefix]) ? $formFieldArray[$fieldNamePrefix] : array());
+               }
+               return $this->serializeAndHashFormFieldArray($formFieldArray);
+       }
+
+       /**
+        * Serialize and hash the form field array
+        *
+        * @param array $formFieldArray form field array to be serialized and hashed
+        *
+        * @return string Hash
+        */
+       protected function serializeAndHashFormFieldArray(array $formFieldArray) {
+               $serializedFormFieldArray = serialize($formFieldArray);
+               return $this->hashService->appendHmac($serializedFormFieldArray);
+       }
+
+       /**
+        * Initialize the property mapping configuration in $controllerArguments if
+        * the trusted properties are set inside the request.
+        *
+        * @param \TYPO3\CMS\Extbase\Mvc\Request $request
+        * @param \TYPO3\CMS\Extbase\Mvc\Controller\Arguments $controllerArguments
+        *
+        * @return void
+        */
+       public function initializePropertyMappingConfigurationFromRequest(\TYPO3\CMS\Extbase\Mvc\Request $request, \TYPO3\CMS\Extbase\Mvc\Controller\Arguments $controllerArguments) {
+               $trustedPropertiesToken = $request->getInternalArgument('__trustedProperties');
+               if (!is_string($trustedPropertiesToken)) {
+                       return;
+               }
+
+               $serializedTrustedProperties = $this->hashService->validateAndStripHmac($trustedPropertiesToken);
+               $trustedProperties = unserialize($serializedTrustedProperties);
+               foreach ($trustedProperties as $propertyName => $propertyConfiguration) {
+                       if (!$controllerArguments->hasArgument($propertyName)) {
+                               continue;
+                       }
+                       $propertyMappingConfiguration = $controllerArguments->getArgument($propertyName)->getPropertyMappingConfiguration();
+                       $this->modifyPropertyMappingConfiguration($propertyConfiguration, $propertyMappingConfiguration);
+               }
+       }
+
+       /**
+        * Modify the passed $propertyMappingConfiguration according to the $propertyConfiguration which
+        * has been generated by Fluid. In detail, if the $propertyConfiguration contains
+        * an __identity field, we allow modification of objects; else we allow creation.
+        *
+        * All other properties are specified as allowed properties.
+        *
+        * @param array $propertyConfiguration
+        * @param \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration $propertyMappingConfiguration
+        *
+        * @return void
+        */
+       protected function modifyPropertyMappingConfiguration($propertyConfiguration, \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration $propertyMappingConfiguration) {
+               if (!is_array($propertyConfiguration)) {
+                       return;
+               }
+
+               if (isset($propertyConfiguration['__identity'])) {
+                       $propertyMappingConfiguration->setTypeConverterOption('TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter', \TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_MODIFICATION_ALLOWED, TRUE);
+                       unset($propertyConfiguration['__identity']);
+               } else {
+                       $propertyMappingConfiguration->setTypeConverterOption('TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter', \TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED, TRUE);
+               }
+
+               foreach ($propertyConfiguration as $innerKey => $innerValue) {
+                       if (is_array($innerValue)) {
+                               $this->modifyPropertyMappingConfiguration($innerValue, $propertyMappingConfiguration->forProperty($innerKey));
+                       }
+                       $propertyMappingConfiguration->allowProperties($innerKey);
+               }
+       }
+}
+
+?>
\ No newline at end of file
index 3e66aaa..1d2ac1b 100644 (file)
@@ -164,7 +164,7 @@ class PropertyMapper implements \TYPO3\CMS\Core\SingletonInterface {
                foreach ($typeConverter->getSourceChildPropertiesToBeConverted($source) as $sourcePropertyName => $sourcePropertyValue) {
                        $targetPropertyName = $configuration->getTargetPropertyName($sourcePropertyName);
                        if (!$configuration->shouldMap($targetPropertyName)) {
-                               continue;
+                               throw new \TYPO3\CMS\Extbase\Property\Exception\InvalidPropertyMappingConfigurationException('It is not allowed to map property "' . $targetPropertyName . '". You need to use $propertyMappingConfiguration->allowProperties(\'' . $targetPropertyName . '\') to enable mapping of this property.', 1355155913);
                        }
                        $targetPropertyType = $typeConverter->getTypeOfChildProperty($targetType, $targetPropertyName, $configuration);
                        $subConfiguration = $configuration->getConfigurationFor($targetPropertyName);
index 6eb215b..19aa396 100644 (file)
@@ -20,10 +20,10 @@ namespace TYPO3\CMS\Extbase\Property;
  *                                                                        *
  * The TYPO3 project - inspiring people to share!                         *
  *                                                                        */
+
 /**
  * Concrete configuration object for the PropertyMapper.
  *
- * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License, version 3 or later
  * @api
  */
 class PropertyMappingConfiguration implements \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface {
@@ -65,9 +65,31 @@ class PropertyMappingConfiguration implements \TYPO3\CMS\Extbase\Property\Proper
        protected $typeConverter = NULL;
 
        /**
+        * List of allowed property names to be converted
+        *
+        * @var array
+        */
+       protected $propertiesToBeMapped = array();
+
+       /**
+        * List of disallowed property names which will be ignored while property mapping
+        *
+        * @var array
+        */
+       protected $propertiesNotToBeMapped = array();
+
+       /**
+        * If TRUE, unknown properties will be mapped.
+        *
+        * @var boolean
+        */
+       protected $mapUnknownProperties = FALSE;
+
+       /**
         * Set the parent PropertyMappingConfiguration. Only used internally!
         *
         * @param \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $parentConfiguration
+        *
         * @return void
         */
        protected function setParent(\TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $parentConfiguration) {
@@ -75,21 +97,74 @@ class PropertyMappingConfiguration implements \TYPO3\CMS\Extbase\Property\Proper
        }
 
        /**
-        * returns TRUE if the given propertyName should be mapped, FALSE otherwise.
+        * The behavior is as follows:
+        *
+        * - if a property has been explicitely forbidden using allowAllPropertiesExcept(...), it is directly rejected
+        * - if a property has been allowed using allowProperties(...), it is directly allowed.
+        * - if allowAllProperties* has been called, we allow unknown properties
+        * - else, return FALSE.
         *
         * @param string $propertyName
-        * @return boolean
+        * @return boolean TRUE if the given propertyName should be mapped, FALSE otherwise.
         * @todo : extend to enable whitelisting / blacklisting of properties.
         * @api
         */
        public function shouldMap($propertyName) {
-               return TRUE;
+               if (isset($this->propertiesNotToBeMapped[$propertyName])) {
+                       return FALSE;
+               }
+               if (isset($this->propertiesToBeMapped[$propertyName])) {
+                       return TRUE;
+               }
+               return $this->mapUnknownProperties;
+       }
+
+       /**
+        * Allow all properties in property mapping, even unknown ones.
+        *
+        * @return void
+        * @api
+        */
+       public function allowAllProperties() {
+               $this->mapUnknownProperties = TRUE;
+       }
+
+       /**
+        * Allow a list of specific properties. All arguments of
+        * allowProperties are used here (varargs).
+        *
+        * Example: allowProperties('title', 'content', 'author')
+        *
+        * @return void
+        * @api
+        */
+       public function allowProperties() {
+               foreach (func_get_args() as $propertyName) {
+                       $this->propertiesToBeMapped[$propertyName] = $propertyName;
+               }
+       }
+
+       /**
+        * Allow all properties during property mapping, but reject a few
+        * selected ones (blacklist).
+        *
+        * Example: allowAllPropertiesExcept('password', 'userGroup')
+        *
+        * @return void
+        * @api
+        */
+       public function allowAllPropertiesExcept() {
+               $this->mapUnknownProperties = TRUE;
+               foreach (func_get_args() as $propertyName) {
+                       $this->propertiesNotToBeMapped[$propertyName] = $propertyName;
+               }
        }
 
        /**
         * Returns the sub-configuration for the passed $propertyName. Must ALWAYS return a valid configuration object!
         *
         * @param string $propertyName
+        *
         * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface the property mapping configuration for the given $propertyName.
         * @api
         */
@@ -104,6 +179,7 @@ class PropertyMappingConfiguration implements \TYPO3\CMS\Extbase\Property\Proper
         * Maps the given $sourcePropertyName to a target property name.
         *
         * @param string $sourcePropertyName
+        *
         * @return string property name of target
         * @api
         */
@@ -117,6 +193,7 @@ class PropertyMappingConfiguration implements \TYPO3\CMS\Extbase\Property\Proper
        /**
         * @param string $typeConverterClassName
         * @param string $key
+        *
         * @return mixed configuration value for the specific $typeConverterClassName. Can be used by Type Converters to fetch converter-specific configuration.
         * @api
         */
@@ -132,6 +209,7 @@ class PropertyMappingConfiguration implements \TYPO3\CMS\Extbase\Property\Proper
         *
         * @param string $sourcePropertyName
         * @param string $targetPropertyName
+        *
         * @return void
         * @api
         */
@@ -144,6 +222,7 @@ class PropertyMappingConfiguration implements \TYPO3\CMS\Extbase\Property\Proper
         *
         * @param string $typeConverter class name of type converter
         * @param array $options
+        *
         * @return void
         * @api
         */
@@ -157,6 +236,7 @@ class PropertyMappingConfiguration implements \TYPO3\CMS\Extbase\Property\Proper
         * @param string $typeConverter class name of type converter
         * @param string $optionKey
         * @param mixed $optionValue
+        *
         * @return void
         * @api
         */
@@ -170,6 +250,7 @@ class PropertyMappingConfiguration implements \TYPO3\CMS\Extbase\Property\Proper
         * $configuration->forProperty('foo.bar')->setTypeConverterOption(....)
         *
         * @param string $propertyPath
+        *
         * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration (or a subclass thereof)
         * @api
         */
@@ -182,6 +263,7 @@ class PropertyMappingConfiguration implements \TYPO3\CMS\Extbase\Property\Proper
         * Traverse the property configuration. Only used by forProperty().
         *
         * @param array $splittedPropertyPath
+        *
         * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration (or a subclass thereof)
         */
        public function traverseProperties(array $splittedPropertyPath) {
@@ -211,6 +293,7 @@ class PropertyMappingConfiguration implements \TYPO3\CMS\Extbase\Property\Proper
         * Set a type converter which should be used for this specific conversion.
         *
         * @param \TYPO3\CMS\Extbase\Property\TypeConverterInterface $typeConverter
+        *
         * @return void
         * @api
         */
@@ -219,4 +302,4 @@ class PropertyMappingConfiguration implements \TYPO3\CMS\Extbase\Property\Proper
        }
 }
 
-?>
\ No newline at end of file
+?>
index 5fee5ef..e449e20 100644 (file)
@@ -39,6 +39,7 @@ class PropertyMappingConfigurationBuilder implements \TYPO3\CMS\Core\SingletonIn
                        \TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED => TRUE,
                        \TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_MODIFICATION_ALLOWED => TRUE
                ));
+               $configuration->allowAllProperties();
                return $configuration;
        }
 }
index 51ee805..5821b09 100644 (file)
@@ -44,6 +44,11 @@ class ActionControllerTest extends \TYPO3\CMS\Extbase\Tests\Unit\BaseTestCase {
         */
        protected $mockUriBuilder;
 
+       /**
+        * @var \TYPO3\CMS\Extbase\Mvc\Controller\MvcPropertyMappingConfigurationService
+        */
+       protected $mockMvcPropertyMappingConfigurationService;
+
        public function setUp() {
                $this->actionController = $this->getAccessibleMock('TYPO3\\CMS\\Extbase\\Mvc\\Controller\\ActionController');
        }
@@ -59,6 +64,7 @@ class ActionControllerTest extends \TYPO3\CMS\Extbase\Tests\Unit\BaseTestCase {
                $mockObjectManager = $this->getMock('TYPO3\\CMS\\Extbase\\Object\\ObjectManagerInterface');
                $mockObjectManager->expects($this->once())->method('create')->with('TYPO3\\CMS\\Extbase\\Mvc\\Web\\Routing\\UriBuilder')->will($this->returnValue($mockUriBuilder));
                $mockResponse = $this->getMock('TYPO3\\CMS\\Extbase\\Mvc\\Web\\Response', array(), array(), '', FALSE);
+               $configurationService = $this->getMock('TYPO3\\CMS\\Extbase\\Mvc\\Controller\MvcPropertyMappingConfigurationService');
                /** @var \TYPO3\CMS\Extbase\Mvc\Controller\ActionController|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface */
                $mockController = $this->getAccessibleMock('TYPO3\\CMS\\Extbase\\Mvc\\Controller\\ActionController', array(
                        'initializeFooAction',
@@ -74,6 +80,7 @@ class ActionControllerTest extends \TYPO3\CMS\Extbase\Tests\Unit\BaseTestCase {
                        'checkRequestHash'
                ), array(), '', FALSE);
                $mockController->_set('objectManager', $mockObjectManager);
+
                $mockController->expects($this->at(0))->method('resolveActionMethodName')->will($this->returnValue('fooAction'));
                $mockController->expects($this->at(1))->method('initializeActionMethodArguments');
                $mockController->expects($this->at(2))->method('initializeActionMethodValidators');
@@ -83,6 +90,10 @@ class ActionControllerTest extends \TYPO3\CMS\Extbase\Tests\Unit\BaseTestCase {
                $mockController->expects($this->at(6))->method('checkRequestHash');
                $mockController->expects($this->at(7))->method('buildControllerContext');
                $mockController->expects($this->at(8))->method('resolveView');
+
+               $mockController->injectMvcPropertyMappingConfigurationService($configurationService);
+               $mockController->_set('arguments', new \TYPO3\CMS\Extbase\Mvc\Controller\Arguments());
+
                $mockController->processRequest($mockRequest, $mockResponse);
                $this->assertSame($mockRequest, $mockController->_get('request'));
                $this->assertSame($mockResponse, $mockController->_get('response'));
index 84ca7b5..7ca4c20 100644 (file)
@@ -53,13 +53,10 @@ class ArgumentTest extends \TYPO3\CMS\Extbase\Tests\Unit\BaseTestCase {
                $this->mockPropertyMapper = $this->getMock('TYPO3\\CMS\\Extbase\\Property\\PropertyMapper');
                $this->simpleValueArgument->injectPropertyMapper($this->mockPropertyMapper);
                $this->objectArgument->injectPropertyMapper($this->mockPropertyMapper);
-               $this->mockConfigurationBuilder = $this->getMock('TYPO3\\CMS\\Extbase\\Property\\PropertyMappingConfigurationBuilder');
-               $this->mockConfiguration = $this->getMock('TYPO3\\CMS\\Extbase\\Property\\PropertyMappingConfigurationInterface');
-               $this->mockConfigurationBuilder->expects($this->any())->method('build')->with('TYPO3\\CMS\\Extbase\\Mvc\\Controller\\MvcPropertyMappingConfiguration')->will($this->returnValue($this->mockConfiguration));
-               $this->simpleValueArgument->injectPropertyMappingConfigurationBuilder($this->mockConfigurationBuilder);
-               $this->objectArgument->injectPropertyMappingConfigurationBuilder($this->mockConfigurationBuilder);
-               $this->simpleValueArgument->initializeObject();
-               $this->objectArgument->initializeObject();
+               $this->mockConfiguration = new \TYPO3\CMS\Extbase\Mvc\Controller\MvcPropertyMappingConfiguration();
+               $propertyMappingConfiguranion = new \TYPO3\CMS\Extbase\Mvc\Controller\MvcPropertyMappingConfiguration();
+               $this->simpleValueArgument->injectPropertyMappingConfiguration($propertyMappingConfiguranion);
+               $this->objectArgument->injectPropertyMappingConfiguration($propertyMappingConfiguranion);
        }
 
        /**
@@ -288,8 +285,9 @@ class ArgumentTest extends \TYPO3\CMS\Extbase\Tests\Unit\BaseTestCase {
         * @test
         * @author Sebastian Kurfürst <sebastian@typo3.org>
         */
-       public function defaultPropertyMappingConfigurationShouldBeFetchable() {
-               $this->assertSame($this->mockConfiguration, $this->simpleValueArgument->getPropertyMappingConfiguration());
+       public function defaultPropertyMappingConfigurationDoesNotAllowCreationOrModificationOfObjects() {
+               $this->assertNull($this->simpleValueArgument->getPropertyMappingConfiguration()->getConfigurationValue('TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter', \TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED));
+               $this->assertNull($this->simpleValueArgument->getPropertyMappingConfiguration()->getConfigurationValue('TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter', \TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_MODIFICATION_ALLOWED));
        }
 
        /**
diff --git a/typo3/sysext/extbase/Tests/Unit/Mvc/Controller/MvcPropertyMappingConfigurationServiceTest.php b/typo3/sysext/extbase/Tests/Unit/Mvc/Controller/MvcPropertyMappingConfigurationServiceTest.php
new file mode 100644 (file)
index 0000000..f47335f
--- /dev/null
@@ -0,0 +1,306 @@
+<?php
+namespace TYPO3\CMS\Extbase\Tests\Unit\Mvc\Controller;
+
+/*                                                                        *
+ * This script belongs to the Extbase framework                           *
+ *                                                                        *
+ * It is free software; you can redistribute it and/or modify it under    *
+ * the terms of the GNU Lesser General Public License as published by the *
+ * Free Software Foundation, either version 3 of the License, or (at your *
+ * option) any later version.                                             *
+ *                                                                        *
+ * This script is distributed in the hope that it will be useful, but     *
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN-    *
+ * TABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser       *
+ * General Public License for more details.                               *
+ *                                                                        *
+ * You should have received a copy of the GNU Lesser General Public       *
+ * License along with the script.                                         *
+ * If not, see http://www.gnu.org/licenses/lgpl.html                      *
+ *                                                                        *
+ * The TYPO3 project - inspiring people to share!                         *
+ *                                                                        */
+
+use TYPO3\CMS\Core\Utility\DebugUtility;
+use TYPO3\CMS\Extbase\Utility\DebuggerUtility;
+
+/**
+ * Testcase for the MVC Property Mapping Configuration Service
+ * @covers \TYPO3\CMS\Extbase\Mvc\Controller\MvcPropertyMappingConfigurationService
+ */
+class MvcPropertyMappingConfigurationServiceTest extends \TYPO3\CMS\Extbase\Tests\Unit\BaseTestCase {
+
+       /**
+        * Data provider for generating the list of trusted properties
+        *
+        * @return array
+        */
+       public function dataProviderForgenerateTrustedPropertiesToken() {
+               return array(
+                       'Simple Case - Empty' => array(
+                               array(),
+                               array(),
+                       ),
+                       'Simple Case - Single Value' => array(
+                               array('field1'),
+                               array('field1' => 1),
+                       ),
+                       'Simple Case - Two Values' => array(
+                               array('field1', 'field2'),
+                               array(
+                                       'field1' => 1,
+                                       'field2' => 1
+                               ),
+                       ),
+                       'Recursion' => array(
+                               array('field1', 'field[subfield1]', 'field[subfield2]'),
+                               array(
+                                       'field1' => 1,
+                                       'field' => array(
+                                               'subfield1' => 1,
+                                               'subfield2' => 1
+                                       )
+                               ),
+                       ),
+                       'recursion with duplicated field name' => array(
+                               array('field1', 'field[subfield1]', 'field[subfield2]', 'field1'),
+                               array(
+                                       'field1' => 1,
+                                       'field' => array(
+                                               'subfield1' => 1,
+                                               'subfield2' => 1
+                                       )
+                               ),
+                       ),
+                       'Recursion with un-named fields at the end (...[]). There, they should be made explicit by increasing the counter' => array(
+                               array('field1', 'field[subfield1][]', 'field[subfield1][]', 'field[subfield2]'),
+                               array(
+                                       'field1' => 1,
+                                       'field' => array(
+                                               'subfield1' => array(
+                                                       0 => 1,
+                                                       1 => 1
+                                               ),
+                                               'subfield2' => 1
+                                       )
+                               ),
+                       ),
+               );
+       }
+
+       /**
+        * Data Provider for invalid values in generating the list of trusted properties,
+        * which should result in an exception
+        *
+        * @return array
+        */
+       public function dataProviderForgenerateTrustedPropertiesTokenWithUnallowedValues() {
+               return array(
+                       'Overriding form fields (string overridden by array) - 1' => array(
+                               array('field1', 'field2', 'field2[bla]', 'field2[blubb]'),
+                       ),
+                       'Overriding form fields (string overridden by array) - 2' => array(
+                               array('field1', 'field2[bla]', 'field2[bla][blubb][blubb]'),
+                       ),
+                       'Overriding form fields (array overridden by string) - 1' => array(
+                               array('field1', 'field2[bla]', 'field2[blubb]', 'field2'),
+                       ),
+                       'Overriding form fields (array overridden by string) - 2' => array(
+                               array('field1', 'field2[bla][blubb][blubb]', 'field2[bla]'),
+                       ),
+                       'Empty [] not as last argument' => array(
+                               array('field1', 'field2[][bla]'),
+                       )
+
+               );
+       }
+
+       /**
+        * @test
+        * @dataProvider dataProviderForgenerateTrustedPropertiesToken
+        */
+       public function generateTrustedPropertiesTokenGeneratesTheCorrectHashesInNormalOperation($input, $expected) {
+               $requestHashService = $this->getMock('TYPO3\CMS\Extbase\Mvc\Controller\MvcPropertyMappingConfigurationService', array('serializeAndHashFormFieldArray'));
+               $requestHashService->expects($this->once())->method('serializeAndHashFormFieldArray')->with($expected);
+               $requestHashService->generateTrustedPropertiesToken($input);
+       }
+
+       /**
+        * @test
+        * @dataProvider dataProviderForgenerateTrustedPropertiesTokenWithUnallowedValues
+        * @expectedException \TYPO3\CMS\Extbase\Security\Exception\InvalidArgumentForHashGenerationException
+        */
+       public function generateTrustedPropertiesTokenThrowsExceptionInWrongCases($input) {
+               $requestHashService = $this->getMock('TYPO3\CMS\Extbase\Mvc\Controller\MvcPropertyMappingConfigurationService', array('serializeAndHashFormFieldArray'));
+               $requestHashService->generateTrustedPropertiesToken($input);
+       }
+
+       /**
+        * @test
+        */
+       public function serializeAndHashFormFieldArrayWorks() {
+               $formFieldArray = array(
+                       'bla' => array(
+                               'blubb' => 1,
+                               'hu' => 1
+                       )
+               );
+               $mockHash = '12345';
+
+               $hashService = $this->getMock($this->buildAccessibleProxy('TYPO3\CMS\Extbase\Mvc\Controller\MvcPropertyMappingConfigurationService'), array('appendHmac'));
+               $hashService->expects($this->once())->method('appendHmac')->with(serialize($formFieldArray))->will($this->returnValue(serialize($formFieldArray) . $mockHash));
+
+               $requestHashService = $this->getMock($this->buildAccessibleProxy('TYPO3\CMS\Extbase\Mvc\Controller\MvcPropertyMappingConfigurationService'), array('dummy'));
+               $requestHashService->_set('hashService', $hashService);
+
+               $expected = serialize($formFieldArray) . $mockHash;
+               $actual = $requestHashService->_call('serializeAndHashFormFieldArray', $formFieldArray);
+               $this->assertEquals($expected, $actual);
+       }
+
+       /**
+        * @test
+        */
+       public function initializePropertyMappingConfigurationDoesNothingIfTrustedPropertiesAreNotSet() {
+               $request = $this->getMockBuilder('TYPO3\CMS\Extbase\Mvc\Request')->setMethods(array('getInternalArgument'))->disableOriginalConstructor()->getMock();
+               $request->expects($this->any())->method('getInternalArgument')->with('__trustedProperties')->will($this->returnValue(NULL));
+               $arguments = new \TYPO3\CMS\Extbase\Mvc\Controller\Arguments();
+
+               $requestHashService = new \TYPO3\CMS\Extbase\Mvc\Controller\MvcPropertyMappingConfigurationService;
+               $requestHashService->initializePropertyMappingConfigurationFromRequest($request, $arguments);
+       }
+
+       /**
+        * @test
+        */
+       public function initializePropertyMappingConfigurationReturnsEarlyIfNoTrustedPropertiesAreSet() {
+               $trustedProperties = array(
+                       'foo' => 1
+               );
+               $this->initializePropertyMappingConfiguration($trustedProperties);
+       }
+
+       /**
+        * @test
+        */
+       public function initializePropertyMappingConfigurationReturnsEarlyIfArgumentIsUnknown() {
+               $trustedProperties = array(
+                       'nonExistingArgument' => 1
+               );
+               $arguments = $this->initializePropertyMappingConfiguration($trustedProperties);
+               $this->assertFalse($arguments->hasArgument('nonExistingArgument'));
+       }
+
+       /**
+        * @test
+        */
+       public function initializePropertyMappingConfigurationSetsModificationAllowedIfIdentityPropertyIsSet() {
+               $trustedProperties = array(
+                       'foo' => array(
+                               '__identity' => 1,
+                               'nested' => array(
+                                       '__identity' => 1,
+                               )
+                       )
+               );
+               $arguments = $this->initializePropertyMappingConfiguration($trustedProperties);
+               $propertyMappingConfiguration = $arguments->getArgument('foo')->getPropertyMappingConfiguration();
+               $this->assertTrue($propertyMappingConfiguration->getConfigurationValue('TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter', \TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_MODIFICATION_ALLOWED), 'ConfigurationValue is not CONFIGURATION_MODIFICATION_ALLOWED at line ' . __LINE__);
+               $this->assertNull($propertyMappingConfiguration->getConfigurationValue('TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter', \TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED), 'ConfigurationValue is not NULL at line ' . __LINE__);
+               $this->assertFalse($propertyMappingConfiguration->shouldMap('someProperty'), 'Value is not FALSE at line ' . __LINE__);
+
+               $this->assertTrue($propertyMappingConfiguration->forProperty('nested')->getConfigurationValue('TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter', \TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_MODIFICATION_ALLOWED), 'ConfigurationValue is not CONFIGURATION_MODIFICATION_ALLOWED at line ' . __LINE__);
+               $this->assertNull($propertyMappingConfiguration->forProperty('nested')->getConfigurationValue('TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter', \TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED), 'ConfigurationValue is not NULL at line ' . __LINE__);
+               $this->assertFalse($propertyMappingConfiguration->forProperty('nested')->shouldMap('someProperty'), 'Value is not FALSE at line ' . __LINE__);
+       }
+
+       /**
+        * @test
+        */
+       public function initializePropertyMappingConfigurationSetsCreationAllowedIfIdentityPropertyIsNotSet() {
+               $trustedProperties = array(
+                       'foo' => array(
+                               'bar' => array()
+                       )
+               );
+               $arguments = $this->initializePropertyMappingConfiguration($trustedProperties);
+               $propertyMappingConfiguration = $arguments->getArgument('foo')->getPropertyMappingConfiguration();
+               $this->assertNull($propertyMappingConfiguration->getConfigurationValue('TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter', \TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_MODIFICATION_ALLOWED));
+               $this->assertTrue($propertyMappingConfiguration->getConfigurationValue('TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter', \TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED));
+               $this->assertFalse($propertyMappingConfiguration->shouldMap('someProperty'));
+
+               $this->assertNull($propertyMappingConfiguration->forProperty('bar')->getConfigurationValue('TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter', \TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_MODIFICATION_ALLOWED));
+               $this->assertTrue($propertyMappingConfiguration->forProperty('bar')->getConfigurationValue('TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter', \TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED));
+               $this->assertFalse($propertyMappingConfiguration->forProperty('bar')->shouldMap('someProperty'));
+       }
+
+       /**
+        * @test
+        */
+       public function initializePropertyMappingConfigurationSetsAllowedFields() {
+               $trustedProperties = array(
+                       'foo' => array(
+                               'bar' => 1
+                       )
+               );
+               $arguments = $this->initializePropertyMappingConfiguration($trustedProperties);
+               $propertyMappingConfiguration = $arguments->getArgument('foo')->getPropertyMappingConfiguration();
+               $this->assertFalse($propertyMappingConfiguration->shouldMap('someProperty'));
+               $this->assertTrue($propertyMappingConfiguration->shouldMap('bar'));
+       }
+
+       /**
+        * @test
+        */
+       public function initializePropertyMappingConfigurationSetsAllowedFieldsRecursively() {
+               $trustedProperties = array(
+                       'foo' => array(
+                               'bar' => array(
+                                       'foo' => 1
+                               )
+                       )
+               );
+               $arguments = $this->initializePropertyMappingConfiguration($trustedProperties);
+               $propertyMappingConfiguration = $arguments->getArgument('foo')->getPropertyMappingConfiguration();
+               $this->assertFalse($propertyMappingConfiguration->shouldMap('someProperty'));
+               $this->assertTrue($propertyMappingConfiguration->shouldMap('bar'));
+               $this->assertTrue($propertyMappingConfiguration->forProperty('bar')->shouldMap('foo'));
+       }
+
+       /**
+        * Helper which initializes the property mapping configuration and returns arguments
+        *
+        * @param array $trustedProperties
+        *
+        * @return \TYPO3\CMS\Extbase\Mvc\Controller\Arguments
+        */
+       protected function initializePropertyMappingConfiguration(array $trustedProperties) {
+               $request = $this->getMockBuilder('TYPO3\CMS\Extbase\Mvc\Request')->setMethods(array('getInternalArgument'))->disableOriginalConstructor()->getMock();
+               $request->expects($this->any())->method('getInternalArgument')->with('__trustedProperties')->will($this->returnValue('fooTrustedProperties'));
+
+               $mockHashService = $this->getMock('TYPO3\CMS\Extbase\Security\Cryptography\HashService', array('validateAndStripHmac'));
+               $mockHashService->expects($this->once())->method('validateAndStripHmac')->with('fooTrustedProperties')->will($this->returnValue(serialize($trustedProperties)));
+
+               $requestHashService = new \TYPO3\CMS\Extbase\Mvc\Controller\MvcPropertyMappingConfigurationService();
+               $requestHashService->injectHashService($mockHashService);
+
+               $mockObjectManager = $this->getMock('TYPO3\\CMS\\Extbase\\Object\\ObjectManagerInterface');
+               $mockArgument = $this->getMock('TYPO3\\CMS\\Extbase\\Mvc\\Controller\\Argument', array('getName'), array(), '', FALSE);
+
+               $propertyMappingConfiguration = new \TYPO3\CMS\Extbase\Mvc\Controller\MvcPropertyMappingConfiguration();
+
+               $mockArgument->injectPropertyMappingConfiguration($propertyMappingConfiguration);
+               $mockArgument->expects($this->any())->method('getName')->will($this->returnValue('foo'));
+               $mockObjectManager->expects($this->once())->method('create')->with('TYPO3\\CMS\\Extbase\\Mvc\\Controller\\Argument')->will($this->returnValue($mockArgument));
+
+               $arguments = new \TYPO3\CMS\Extbase\Mvc\Controller\Arguments();
+               $arguments->injectObjectManager($mockObjectManager);
+               $arguments->addNewArgument('foo');
+
+               $requestHashService->initializePropertyMappingConfigurationFromRequest($request, $arguments);
+
+               return $arguments;
+       }
+}
+
+?>
\ No newline at end of file
index 1cb810b..ff53200 100644 (file)
@@ -23,7 +23,6 @@ namespace TYPO3\CMS\Extbase\Tests\Unit\Property;
 /**
  * Testcase for the Property Mapper
  *
- * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License, version 3 or later
  * @covers \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration
  */
 class PropertyMappingConfigurationTest extends \TYPO3\CMS\Extbase\Tests\Unit\BaseTestCase {
@@ -39,7 +38,6 @@ class PropertyMappingConfigurationTest extends \TYPO3\CMS\Extbase\Tests\Unit\Bas
 
        /**
         * @test
-        * @author Sebastian Kurfürst <sebastian@typo3.org>
         * @covers \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration::getTargetPropertyName
         */
        public function getTargetPropertyNameShouldReturnTheUnmodifiedPropertyNameWithoutConfiguration() {
@@ -50,16 +48,45 @@ class PropertyMappingConfigurationTest extends \TYPO3\CMS\Extbase\Tests\Unit\Bas
        /**
         * @test
         * @covers \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration::shouldMap
-        * @author Sebastian Kurfürst <sebastian@typo3.org>
         */
-       public function shouldMapReturnsTrue() {
+       public function shouldMapReturnsFalseByDefault() {
+               $this->assertFalse($this->propertyMappingConfiguration->shouldMap('someSourceProperty'));
+               $this->assertFalse($this->propertyMappingConfiguration->shouldMap('someOtherSourceProperty'));
+       }
+
+       /**
+        * @test
+        * @covers \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration::shouldMap
+        */
+       public function shouldMapReturnsTrueIfConfigured() {
+               $this->propertyMappingConfiguration->allowAllProperties();
                $this->assertTrue($this->propertyMappingConfiguration->shouldMap('someSourceProperty'));
                $this->assertTrue($this->propertyMappingConfiguration->shouldMap('someOtherSourceProperty'));
        }
 
        /**
         * @test
-        * @author Sebastian Kurfürst <sebastian@typo3.org>
+        * @covers \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration::shouldMap
+        */
+       public function shouldMapReturnsTrueForAllowedProperties() {
+               $this->propertyMappingConfiguration->allowProperties('someSourceProperty', 'someOtherProperty');
+               $this->assertTrue($this->propertyMappingConfiguration->shouldMap('someSourceProperty'));
+               $this->assertTrue($this->propertyMappingConfiguration->shouldMap('someOtherProperty'));
+       }
+
+       /**
+        * @test
+        * @covers \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration::shouldMap
+        */
+       public function shouldMapReturnsFalseForBlacklistedProperties() {
+               $this->propertyMappingConfiguration->allowAllPropertiesExcept('someSourceProperty', 'someOtherProperty');
+               $this->assertFalse($this->propertyMappingConfiguration->shouldMap('someSourceProperty'));
+               $this->assertFalse($this->propertyMappingConfiguration->shouldMap('someOtherProperty'));
+               $this->assertTrue($this->propertyMappingConfiguration->shouldMap('someOtherPropertyWhichHasNotBeenConfigured'));
+       }
+
+       /**
+        * @test
         */
        public function setTypeConverterOptionsCanBeRetrievedAgain() {
                $this->propertyMappingConfiguration->setTypeConverterOptions('someConverter', array('k1' => 'v1', 'k2' => 'v2'));
@@ -69,7 +96,6 @@ class PropertyMappingConfigurationTest extends \TYPO3\CMS\Extbase\Tests\Unit\Bas
 
        /**
         * @test
-        * @author Sebastian Kurfürst <sebastian@typo3.org>
         */
        public function inexistentTypeConverterOptionsReturnNull() {
                $this->assertNull($this->propertyMappingConfiguration->getConfigurationValue('foo', 'bar'));
@@ -77,7 +103,6 @@ class PropertyMappingConfigurationTest extends \TYPO3\CMS\Extbase\Tests\Unit\Bas
 
        /**
         * @test
-        * @author Sebastian Kurfürst <sebastian@typo3.org>
         */
        public function setTypeConverterOptionsShouldOverrideAlreadySetOptions() {
                $this->propertyMappingConfiguration->setTypeConverterOptions('someConverter', array('k1' => 'v1', 'k2' => 'v2'));
@@ -88,7 +113,6 @@ class PropertyMappingConfigurationTest extends \TYPO3\CMS\Extbase\Tests\Unit\Bas
 
        /**
         * @test
-        * @author Sebastian Kurfürst <sebastian@typo3.org>
         */
        public function setTypeConverterOptionShouldOverrideAlreadySetOptions() {
                $this->propertyMappingConfiguration->setTypeConverterOptions('someConverter', array('k1' => 'v1', 'k2' => 'v2'));
@@ -99,7 +123,6 @@ class PropertyMappingConfigurationTest extends \TYPO3\CMS\Extbase\Tests\Unit\Bas
 
        /**
         * @test
-        * @author Sebastian Kurfürst <sebastian@typo3.org>
         */
        public function getTypeConverterReturnsNullIfNoTypeConverterSet() {
                $this->assertNull($this->propertyMappingConfiguration->getTypeConverter());
@@ -107,7 +130,6 @@ class PropertyMappingConfigurationTest extends \TYPO3\CMS\Extbase\Tests\Unit\Bas
 
        /**
         * @test
-        * @author Sebastian Kurfürst <sebastian@typo3.org>
         */
        public function getTypeConverterReturnsTypeConverterIfItHasBeenSet() {
                $mockTypeConverter = $this->getMock('TYPO3\\CMS\\Extbase\\Property\\TypeConverterInterface');
@@ -117,7 +139,6 @@ class PropertyMappingConfigurationTest extends \TYPO3\CMS\Extbase\Tests\Unit\Bas
 
        /**
         * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration
-        * @author Sebastian Kurfürst <sebastian@typo3.org>
         */
        protected function buildChildConfigurationForSingleProperty() {
                $childConfiguration = $this->propertyMappingConfiguration->forProperty('key1.key2');
@@ -127,7 +148,6 @@ class PropertyMappingConfigurationTest extends \TYPO3\CMS\Extbase\Tests\Unit\Bas
 
        /**
         * @test
-        * @author Sebastian Kurfürst <sebastian@typo3.org>
         */
        public function getTargetPropertyNameShouldRespectMapping() {
                $this->propertyMappingConfiguration->setMapping('k1', 'k1a');