/** * Adds a CSRF token as argument in the URI builder * * @FLOW3\Before("setting(TYPO3.FLOW3.security.enable) && method(TYPO3\FLOW3\Mvc\Routing\UriBuilder->build())") * @param \TYPO3\FLOW3\Aop\JoinPointInterface $joinPoint The current join point * @return void */ public function addCsrfTokenToUri(\TYPO3\FLOW3\Aop\JoinPointInterface $joinPoint) { $uriBuilder = $joinPoint->getProxy(); $arguments = $joinPoint->getMethodArgument('arguments'); $packageKey = isset($arguments['@package']) ? $arguments['@package'] : ''; $subpackageKey = isset($arguments['@subpackage']) ? $arguments['@subpackage'] : ''; $controllerName = isset($arguments['@controller']) ? $arguments['@controller'] : 'Standard'; $actionName = (isset($arguments['@action']) ? $arguments['@action'] : 'index') . 'Action'; $possibleObjectName = '@package\\@subpackage\\Controller\\@controllerController'; $possibleObjectName = str_replace('@package', str_replace('.', '\\', $packageKey), $possibleObjectName); $possibleObjectName = str_replace('@subpackage', $subpackageKey, $possibleObjectName); $possibleObjectName = str_replace('@controller', $controllerName, $possibleObjectName); $possibleObjectName = str_replace('\\\\', '\\', $possibleObjectName); $lowercaseObjectName = strtolower($possibleObjectName); $className = $this->objectManager->getClassNameByObjectName($this->objectManager->getCaseSensitiveObjectName($lowercaseObjectName)); if ($this->policyService->hasPolicyEntryForMethod($className, $actionName) && !$this->reflectionService->isMethodAnnotatedWith($className, $actionName, 'TYPO3\\FLOW3\\Annotations\\SkipCsrfProtection')) { $internalArguments = $uriBuilder->getArguments(); $internalArguments['__csrfToken'] = $this->securityContext->getCsrfProtectionToken(); $uriBuilder->setArguments($internalArguments); } }
/** * Matches a \TYPO3\FLOW3\Mvc\RequestInterface against the configured CSRF pattern rules and searches for invalid * csrf tokens. * * @param \TYPO3\FLOW3\Mvc\RequestInterface $request The request that should be matched * @return boolean TRUE if the pattern matched, FALSE otherwise * @throws \TYPO3\FLOW3\Security\Exception\AuthenticationRequiredException */ public function matchRequest(\TYPO3\FLOW3\Mvc\RequestInterface $request) { if ($this->authenticationManager->isAuthenticated() === FALSE) { return FALSE; } $controllerClassName = $this->objectManager->getClassNameByObjectName($request->getControllerObjectName()); $actionName = $request->getControllerActionName() . 'Action'; if ($this->policyService->hasPolicyEntryForMethod($controllerClassName, $actionName) && !$this->reflectionService->isMethodTaggedWith($controllerClassName, $actionName, 'skipcsrfprotection')) { $internalArguments = $request->getInternalArguments(); if (!isset($internalArguments['__csrfToken'])) { return TRUE; } $csrfToken = $internalArguments['__csrfToken']; if (!$this->securityContext->hasCsrfProtectionTokens()) { throw new \TYPO3\FLOW3\Security\Exception\AuthenticationRequiredException('No tokens in security context, possible session timeout', 1317309673); } if ($this->securityContext->isCsrfProtectionTokenValid($csrfToken) === FALSE) { return TRUE; } } return FALSE; }
/** * Returns the name of the action the controller is supposed to execute. * * @return string Action name * @api */ public function getControllerActionName() { $controllerObjectName = $this->getControllerObjectName(); if ($controllerObjectName !== '' && $this->controllerActionName === strtolower($this->controllerActionName)) { $controllerClassName = $this->objectManager->getClassNameByObjectName($controllerObjectName); $lowercaseActionMethodName = strtolower($this->controllerActionName) . 'action'; foreach (get_class_methods($controllerClassName) as $existingMethodName) { if (strtolower($existingMethodName) === $lowercaseActionMethodName) { $this->controllerActionName = substr($existingMethodName, 0, -6); break; } } } return $this->controllerActionName; }
/** * Builds a new instance of $objectType with the given $possibleConstructorArgumentValues. * If constructor argument values are missing from the given array the method looks for a * default value in the constructor signature. * * Furthermore, the constructor arguments are removed from $possibleConstructorArgumentValues * * @param array &$possibleConstructorArgumentValues * @param string $objectType * @return object The created instance * @throws \TYPO3\FLOW3\Property\Exception\InvalidTargetException if a required constructor argument is missing */ protected function buildObject(array &$possibleConstructorArgumentValues, $objectType) { $className = $this->objectManager->getClassNameByObjectName($objectType); if ($this->reflectionService->hasMethod($className, '__construct')) { $constructorSignature = $this->reflectionService->getMethodParameters($className, '__construct'); $constructorArguments = array(); foreach ($constructorSignature as $constructorArgumentName => $constructorArgumentInformation) { if (array_key_exists($constructorArgumentName, $possibleConstructorArgumentValues)) { $constructorArguments[] = $possibleConstructorArgumentValues[$constructorArgumentName]; unset($possibleConstructorArgumentValues[$constructorArgumentName]); } elseif ($constructorArgumentInformation['optional'] === TRUE) { $constructorArguments[] = $constructorArgumentInformation['defaultValue']; } else { throw new \TYPO3\FLOW3\Property\Exception\InvalidTargetException('Missing constructor argument "' . $constructorArgumentName . '" for object of type "' . $objectType . '".', 1268734872); } } $classReflection = new \ReflectionClass($className); return $classReflection->newInstanceArgs($constructorArguments); } else { return new $className(); } }
/** * Reconstitutes an object from a serialized object without calling the constructor. * * @param string $objectHash Identifier of the serialized object * @param array $objectData The object data array * @return object */ protected function reconstituteObject($objectHash, array $objectData) { if (isset($this->reconstitutedObjects[$objectHash])) { return $this->reconstitutedObjects[$objectHash]; } $className = $this->objectManager->getClassNameByObjectName($objectData[self::CLASSNAME]); $object = unserialize('O:' . strlen($className) . ':"' . $className . '":0:{};'); $this->reconstitutedObjects[$objectHash] = $object; foreach ($objectData[self::PROPERTIES] as $propertyName => $propertyData) { switch ($propertyData[self::TYPE]) { case 'simple': $propertyValue = $propertyData[self::VALUE]; break; case 'array': $propertyValue = $this->reconstituteArray($propertyData[self::VALUE]); break; case 'Collection': $propertyValue = new $propertyData[self::CLASSNAME]($this->reconstituteArray($propertyData[self::VALUE])); break; case 'ArrayObject': $propertyValue = new \ArrayObject($this->reconstituteArray($propertyData[self::VALUE])); break; case 'object': $propertyValue = $this->reconstituteObject($propertyData[self::VALUE], $this->objectsAsArray[$propertyData[self::VALUE]]); break; case 'SplObjectStorage': $propertyValue = $this->reconstituteSplObjectStorage($propertyData[self::VALUE]); break; case 'persistenceObject': list($propertyClassName, $propertyUuid) = explode(':', $propertyData[self::VALUE]); $propertyValue = $this->reconstitutePersistenceObject($propertyClassName, $propertyUuid); break; } $reflectionProperty = new \ReflectionProperty(get_class($object), $propertyName); $reflectionProperty->setAccessible(TRUE); $reflectionProperty->setValue($object, $propertyValue); } return $object; }
/** * Dispatches a signal by calling the registered Slot methods * * @param string $signalClassName Name of the class containing the signal * @param string $signalName Name of the signal * @param array $signalArguments arguments passed to the signal method * @return void * @throws \TYPO3\FLOW3\SignalSlot\Exception\InvalidSlotException if the slot is not valid * @api */ public function dispatch($signalClassName, $signalName, array $signalArguments = array()) { if (!isset($this->slots[$signalClassName][$signalName])) { return; } foreach ($this->slots[$signalClassName][$signalName] as $slotInformation) { if (isset($slotInformation['object'])) { $object = $slotInformation['object']; } elseif (substr($slotInformation['method'], 0, 2) === '::') { if (!isset($this->objectManager)) { if (is_callable($slotInformation['class'] . $slotInformation['method'])) { $object = $slotInformation['class']; } else { throw new \TYPO3\FLOW3\SignalSlot\Exception\InvalidSlotException(sprintf('Cannot dispatch %s::%s to class %s. The object manager is not yet available in the Signal Slot Dispatcher and therefore it cannot dispatch classes.', $signalClassName, $signalName, $slotInformation['class']), 1298113624); } } else { $object = $this->objectManager->getClassNameByObjectName($slotInformation['class']); } $slotInformation['method'] = substr($slotInformation['method'], 2); } else { if (!isset($this->objectManager)) { throw new \TYPO3\FLOW3\SignalSlot\Exception\InvalidSlotException(sprintf('Cannot dispatch %s::%s to class %s. The object manager is not yet available in the Signal Slot Dispatcher and therefore it cannot dispatch classes.', $signalClassName, $signalName, $slotInformation['class']), 1298113624); } if (!$this->objectManager->isRegistered($slotInformation['class'])) { throw new \TYPO3\FLOW3\SignalSlot\Exception\InvalidSlotException('The given class "' . $slotInformation['class'] . '" is not a registered object.', 1245673367); } $object = $this->objectManager->get($slotInformation['class']); } if ($slotInformation['passSignalInformation'] === TRUE) { $signalArguments[] = $signalClassName . '::' . $signalName; } if (!method_exists($object, $slotInformation['method'])) { throw new \TYPO3\FLOW3\SignalSlot\Exception\InvalidSlotException('The slot method ' . get_class($object) . '->' . $slotInformation['method'] . '() does not exist.', 1245673368); } call_user_func_array(array($object, $slotInformation['method']), $signalArguments); } }