hashService = $hashService; } /** * Generate a request hash for a list of form fields */ public function generateTrustedPropertiesToken(array $formFieldNames, string $fieldNamePrefix = ''): string { $formFieldArray = []; foreach ($formFieldNames as $formField) { $formFieldParts = explode('[', $formField); $currentPosition = &$formFieldArray; $formFieldPartsCount = count($formFieldParts); for ($i = 0; $i < $formFieldPartsCount; $i++) { $formFieldPart = $formFieldParts[$i]; $formFieldPart = rtrim($formFieldPart, ']'); if (!is_array($currentPosition)) { throw new 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 === $formFieldPartsCount - 1) { if (isset($currentPosition[$formFieldPart]) && is_array($currentPosition[$formFieldPart])) { throw new 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 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] = []; } $currentPosition = &$currentPosition[$formFieldPart]; } } } if ($fieldNamePrefix !== '') { $formFieldArray = ($formFieldArray[$fieldNamePrefix] ?? []); } return $this->encodeAndHashFormFieldArray($formFieldArray); } /** * Encode and hash the form field array */ protected function encodeAndHashFormFieldArray(array $formFieldArray): string { $encodedFormFieldArray = json_encode($formFieldArray); return $this->hashService->appendHmac($encodedFormFieldArray, HashScope::TrustedProperties->prefix(), HashAlgo::SHA3_256); } /** * Initialize the property mapping configuration in $controllerArguments if * the trusted properties are set inside the request. * * @throws BadRequestException */ public function initializePropertyMappingConfigurationFromRequest(Request $request, Arguments $controllerArguments): void { /** @var ExtbaseRequestParameters $extbaseRequestParameters */ $extbaseRequestParameters = $request->getAttribute('extbase'); $trustedPropertiesToken = $extbaseRequestParameters->getInternalArgument('__trustedProperties'); if (!is_string($trustedPropertiesToken)) { return; } try { $encodedTrustedProperties = $this->hashService->validateAndStripHmac($trustedPropertiesToken, HashScope::TrustedProperties->prefix(), HashAlgo::SHA3_256); } catch (InvalidHashStringException $e) { throw new BadRequestException('The HMAC of the form could not be validated.', 1581862822); } $trustedProperties = json_decode($encodedTrustedProperties, true); if (!is_array($trustedProperties)) { if (str_starts_with($encodedTrustedProperties, 'a:')) { throw new BadRequestException('Trusted properties used outdated serialization format instead json.', 1699604555); } throw new BadRequestException('The HMAC of the form could not be utilized.', 1691267306); } foreach ($trustedProperties as $propertyName => $propertyConfiguration) { $propertyName = (string)$propertyName; if (!$controllerArguments->hasArgument($propertyName) || !is_array($propertyConfiguration)) { 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. */ protected function modifyPropertyMappingConfiguration( array $propertyConfiguration, PropertyMappingConfigurationInterface $propertyMappingConfiguration ): void { if (isset($propertyConfiguration['__identity'])) { $propertyMappingConfiguration->setTypeConverterOption(PersistentObjectConverter::class, PersistentObjectConverter::CONFIGURATION_MODIFICATION_ALLOWED, true); unset($propertyConfiguration['__identity']); } else { $propertyMappingConfiguration->setTypeConverterOption(PersistentObjectConverter::class, PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED, true); } foreach ($propertyConfiguration as $innerKey => $innerValue) { if (is_array($innerValue)) { $this->modifyPropertyMappingConfiguration( $innerValue, $propertyMappingConfiguration->forProperty((string)$innerKey) ); } $propertyMappingConfiguration->allowProperties($innerKey); } } }