romm /
formz
This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | /* |
||
| 3 | * 2017 Romain CANON <[email protected]> |
||
| 4 | * |
||
| 5 | * This file is part of the TYPO3 FormZ project. |
||
| 6 | * It is free software; you can redistribute it and/or modify it |
||
| 7 | * under the terms of the GNU General Public License, either |
||
| 8 | * version 3 of the License, or any later version. |
||
| 9 | * |
||
| 10 | * For the full copyright and license information, see: |
||
| 11 | * http://www.gnu.org/licenses/gpl-3.0.html |
||
| 12 | */ |
||
| 13 | |||
| 14 | namespace Romm\Formz\Controller; |
||
| 15 | |||
| 16 | use Exception; |
||
| 17 | use Romm\Formz\Configuration\Form\Field\Validation\Validation; |
||
| 18 | use Romm\Formz\Core\Core; |
||
| 19 | use Romm\Formz\Error\AjaxResult; |
||
| 20 | use Romm\Formz\Error\FormzMessageInterface; |
||
| 21 | use Romm\Formz\Exceptions\ClassNotFoundException; |
||
| 22 | use Romm\Formz\Exceptions\EntryNotFoundException; |
||
| 23 | use Romm\Formz\Exceptions\InvalidArgumentTypeException; |
||
| 24 | use Romm\Formz\Exceptions\InvalidConfigurationException; |
||
| 25 | use Romm\Formz\Exceptions\MissingArgumentException; |
||
| 26 | use Romm\Formz\Form\FormInterface; |
||
| 27 | use Romm\Formz\Form\FormObject; |
||
| 28 | use Romm\Formz\Form\FormObjectFactory; |
||
| 29 | use Romm\Formz\Service\ContextService; |
||
| 30 | use Romm\Formz\Service\ExtensionService; |
||
| 31 | use Romm\Formz\Service\MessageService; |
||
| 32 | use Romm\Formz\Validation\DataObject\ValidatorDataObject; |
||
| 33 | use TYPO3\CMS\Extbase\Error\Error; |
||
| 34 | use TYPO3\CMS\Extbase\Mvc\Controller\ActionController; |
||
| 35 | use TYPO3\CMS\Extbase\Mvc\RequestInterface; |
||
| 36 | use TYPO3\CMS\Extbase\Mvc\ResponseInterface; |
||
| 37 | use TYPO3\CMS\Extbase\Mvc\Web\Request; |
||
| 38 | use TYPO3\CMS\Extbase\Mvc\Web\Response; |
||
| 39 | use TYPO3\CMS\Extbase\Reflection\ObjectAccess; |
||
| 40 | use TYPO3\CMS\Extbase\Validation\Validator\ValidatorInterface; |
||
| 41 | |||
| 42 | class AjaxValidationController extends ActionController |
||
| 43 | { |
||
| 44 | const DEFAULT_ERROR_MESSAGE_KEY = 'default_error_message'; |
||
| 45 | |||
| 46 | /** |
||
| 47 | * @var Request |
||
| 48 | */ |
||
| 49 | protected $request; |
||
| 50 | |||
| 51 | /** |
||
| 52 | * @var Response |
||
| 53 | */ |
||
| 54 | protected $response; |
||
| 55 | |||
| 56 | /** |
||
| 57 | * @var bool |
||
| 58 | */ |
||
| 59 | protected $protectedRequestMode = true; |
||
| 60 | |||
| 61 | /** |
||
| 62 | * @var string |
||
| 63 | */ |
||
| 64 | protected $formClassName; |
||
| 65 | |||
| 66 | /** |
||
| 67 | * @var string |
||
| 68 | */ |
||
| 69 | protected $formName; |
||
| 70 | |||
| 71 | /** |
||
| 72 | * @var string |
||
| 73 | */ |
||
| 74 | protected $fieldName; |
||
| 75 | |||
| 76 | /** |
||
| 77 | * @var string |
||
| 78 | */ |
||
| 79 | protected $validatorName; |
||
| 80 | |||
| 81 | /** |
||
| 82 | * @var FormInterface |
||
| 83 | */ |
||
| 84 | protected $form; |
||
| 85 | |||
| 86 | /** |
||
| 87 | * @var FormObject |
||
| 88 | */ |
||
| 89 | protected $formObject; |
||
| 90 | |||
| 91 | /** |
||
| 92 | * @var AjaxResult |
||
| 93 | */ |
||
| 94 | protected $result; |
||
| 95 | |||
| 96 | /** |
||
| 97 | * @var Validation |
||
| 98 | */ |
||
| 99 | protected $validation; |
||
| 100 | |||
| 101 | /** |
||
| 102 | * The only accepted method for the request is `POST`. |
||
| 103 | */ |
||
| 104 | public function initializeAction() |
||
| 105 | { |
||
| 106 | if ($this->request->getMethod() !== 'POST') { |
||
| 107 | $this->throwStatus(400); |
||
| 108 | } |
||
| 109 | } |
||
| 110 | |||
| 111 | /** |
||
| 112 | * Will process the request, but also prevent any external message to be |
||
| 113 | * displayed, and catch any exception that could occur during the |
||
| 114 | * validation. |
||
| 115 | * |
||
| 116 | * @param RequestInterface $request |
||
| 117 | * @param ResponseInterface $response |
||
| 118 | * @throws Exception |
||
| 119 | */ |
||
| 120 | public function processRequest(RequestInterface $request, ResponseInterface $response) |
||
| 121 | { |
||
| 122 | $this->result = new AjaxResult; |
||
| 123 | |||
| 124 | try { |
||
| 125 | $this->processRequestParent($request, $response); |
||
| 126 | } catch (Exception $exception) { |
||
| 127 | if (false === $this->protectedRequestMode) { |
||
| 128 | throw $exception; |
||
| 129 | } |
||
| 130 | |||
| 131 | $this->result->clear(); |
||
| 132 | |||
| 133 | $errorMessage = ExtensionService::get()->isInDebugMode() |
||
| 134 | ? $this->getDebugMessageForException($exception) |
||
| 135 | : ContextService::get()->translate(self::DEFAULT_ERROR_MESSAGE_KEY); |
||
| 136 | |||
| 137 | $error = new Error($errorMessage, 1490176818); |
||
| 138 | $this->result->addError($error); |
||
| 139 | $this->result->setData('errorCode', $exception->getCode()); |
||
| 140 | } |
||
| 141 | |||
| 142 | // Cleaning every external message. |
||
| 143 | ob_clean(); |
||
| 144 | |||
| 145 | $this->injectResultInResponse(); |
||
| 146 | } |
||
| 147 | |||
| 148 | /** |
||
| 149 | * Will take care of adding a new argument to the request, based on the form |
||
| 150 | * name and the form class name found in the request arguments. |
||
| 151 | */ |
||
| 152 | protected function initializeActionMethodValidators() |
||
| 153 | { |
||
| 154 | $this->initializeActionMethodValidatorsParent(); |
||
| 155 | |||
| 156 | $request = $this->getRequest(); |
||
| 157 | |||
| 158 | if (false === $request->hasArgument('name')) { |
||
| 159 | throw MissingArgumentException::ajaxControllerNameArgumentNotSet(); |
||
| 160 | } |
||
| 161 | |||
| 162 | if (false === $request->hasArgument('className')) { |
||
| 163 | throw MissingArgumentException::ajaxControllerClassNameArgumentNotSet(); |
||
| 164 | } |
||
| 165 | |||
| 166 | $className = $request->getArgument('className'); |
||
| 167 | |||
| 168 | if (false === class_exists($className)) { |
||
| 169 | throw ClassNotFoundException::ajaxControllerFormClassNameNotFound($className); |
||
| 170 | } |
||
| 171 | |||
| 172 | if (false === in_array(FormInterface::class, class_implements($className))) { |
||
| 173 | throw InvalidArgumentTypeException::ajaxControllerWrongFormType($className); |
||
| 174 | } |
||
| 175 | |||
| 176 | $this->arguments->addNewArgument($request->getArgument('name'), $className, true); |
||
| 177 | } |
||
| 178 | |||
| 179 | /** |
||
| 180 | * Main action that will process the field validation. |
||
| 181 | * |
||
| 182 | * @param string $name |
||
| 183 | * @param string $className |
||
| 184 | * @param string $fieldName |
||
| 185 | * @param string $validatorName |
||
| 186 | */ |
||
| 187 | public function runAction($name, $className, $fieldName, $validatorName) |
||
| 188 | { |
||
| 189 | $this->formName = $name; |
||
| 190 | $this->formClassName = $className; |
||
| 191 | $this->fieldName = $fieldName; |
||
| 192 | $this->validatorName = $validatorName; |
||
| 193 | $this->form = $this->getForm(); |
||
| 194 | |||
| 195 | $this->formObject = $this->getFormObject(); |
||
| 196 | $this->formObject->setForm($this->form); |
||
| 197 | |||
| 198 | $this->validation = $this->getFieldValidation(); |
||
| 199 | |||
| 200 | $validatorDataObject = new ValidatorDataObject($this->formObject, $this->validation); |
||
| 201 | |||
| 202 | /** @var ValidatorInterface $validator */ |
||
| 203 | $validator = Core::instantiate( |
||
| 204 | $this->validation->getClassName(), |
||
| 205 | $this->validation->getOptions(), |
||
| 206 | $validatorDataObject |
||
| 207 | ); |
||
| 208 | |||
| 209 | $fieldValue = ObjectAccess::getProperty($this->form, $this->fieldName); |
||
| 210 | $result = $validator->validate($fieldValue); |
||
| 211 | |||
| 212 | $this->result->merge($result); |
||
| 213 | } |
||
| 214 | |||
| 215 | /** |
||
| 216 | * @return Validation |
||
| 217 | * @throws EntryNotFoundException |
||
| 218 | * @throws InvalidConfigurationException |
||
| 219 | */ |
||
| 220 | protected function getFieldValidation() |
||
| 221 | { |
||
| 222 | $validationResult = $this->formObject->getConfigurationValidationResult(); |
||
| 223 | |||
| 224 | if (true === $validationResult->hasErrors()) { |
||
| 225 | throw InvalidConfigurationException::ajaxControllerInvalidFormConfiguration(); |
||
| 226 | } |
||
| 227 | |||
| 228 | $formConfiguration = $this->formObject->getConfiguration(); |
||
| 229 | |||
| 230 | if (false === $formConfiguration->hasField($this->fieldName)) { |
||
| 231 | throw EntryNotFoundException::ajaxControllerFieldNotFound($this->fieldName, $this->formObject); |
||
| 232 | } |
||
| 233 | |||
| 234 | $field = $formConfiguration->getField($this->fieldName); |
||
| 235 | |||
| 236 | if (false === $field->hasValidation($this->validatorName)) { |
||
| 237 | throw EntryNotFoundException::ajaxControllerValidationNotFoundForField($this->validatorName, $this->fieldName); |
||
| 238 | } |
||
| 239 | |||
| 240 | $fieldValidationConfiguration = $field->getValidationByName($this->validatorName); |
||
| 241 | |||
| 242 | if (false === $fieldValidationConfiguration->doesUseAjax()) { |
||
| 243 | throw InvalidConfigurationException::ajaxControllerAjaxValidationNotActivated($this->validatorName, $this->fieldName); |
||
| 244 | } |
||
| 245 | |||
| 246 | return $fieldValidationConfiguration; |
||
| 247 | } |
||
| 248 | |||
| 249 | /** |
||
| 250 | * Fetches errors/warnings/notices in the result, and put them in the JSON |
||
| 251 | * response. |
||
| 252 | */ |
||
| 253 | protected function injectResultInResponse() |
||
| 254 | { |
||
| 255 | $validationName = $this->validation instanceof Validation |
||
| 256 | ? $this->validation->getName() |
||
| 257 | : 'default'; |
||
| 258 | |||
| 259 | $validationResult = MessageService::get()->sanitizeValidatorResult($this->result, $validationName); |
||
| 260 | |||
| 261 | $result = [ |
||
| 262 | 'success' => !$this->result->hasErrors(), |
||
| 263 | 'data' => $this->result->getData(), |
||
| 264 | 'messages' => [ |
||
| 265 | 'errors' => $this->formatMessages($validationResult->getErrors()), |
||
| 266 | 'warnings' => $this->formatMessages($validationResult->getWarnings()), |
||
| 267 | 'notices' => $this->formatMessages($validationResult->getNotices()) |
||
| 268 | ] |
||
| 269 | ]; |
||
| 270 | |||
| 271 | $this->setUpResponseResult($result); |
||
| 272 | } |
||
| 273 | |||
| 274 | /** |
||
| 275 | * @param array $result |
||
| 276 | */ |
||
| 277 | protected function setUpResponseResult(array $result) |
||
| 278 | { |
||
| 279 | $this->response->setHeader('Content-Type', 'application/json'); |
||
| 280 | $this->response->setContent(json_encode($result)); |
||
| 281 | |||
| 282 | Core::get()->getPageController()->setContentType('application/json'); |
||
| 283 | } |
||
| 284 | |||
| 285 | /** |
||
| 286 | * @param FormzMessageInterface[] $messages |
||
| 287 | * @return array |
||
| 288 | */ |
||
| 289 | protected function formatMessages(array $messages) |
||
| 290 | { |
||
| 291 | $sortedMessages = []; |
||
| 292 | |||
| 293 | foreach ($messages as $message) { |
||
| 294 | $sortedMessages[$message->getMessageKey()] = $message->getMessage(); |
||
|
0 ignored issues
–
show
|
|||
| 295 | } |
||
| 296 | |||
| 297 | return $sortedMessages; |
||
| 298 | } |
||
| 299 | |||
| 300 | /** |
||
| 301 | * Wrapper for unit tests. |
||
| 302 | * |
||
| 303 | * @param RequestInterface $request |
||
| 304 | * @param ResponseInterface $response |
||
| 305 | */ |
||
| 306 | protected function processRequestParent(RequestInterface $request, ResponseInterface $response) |
||
| 307 | { |
||
| 308 | parent::processRequest($request, $response); |
||
|
0 ignored issues
–
show
It seems like you call parent on a different method (
processRequest() instead of processRequestParent()). Are you sure this is correct? If so, you might want to change this to $this->processRequest().
This check looks for a call to a parent method whose name is different than the method from which it is called. Consider the following code: class Daddy
{
protected function getFirstName()
{
return "Eidur";
}
protected function getSurName()
{
return "Gudjohnsen";
}
}
class Son
{
public function getFirstName()
{
return parent::getSurname();
}
}
The Loading history...
|
|||
| 309 | } |
||
| 310 | |||
| 311 | /** |
||
| 312 | * Wrapper for unit tests. |
||
| 313 | */ |
||
| 314 | protected function initializeActionMethodValidatorsParent() |
||
| 315 | { |
||
| 316 | parent::initializeActionMethodValidators(); |
||
|
0 ignored issues
–
show
It seems like you call parent on a different method (
initializeActionMethodValidators() instead of initializeActionMethodValidatorsParent()). Are you sure this is correct? If so, you might want to change this to $this->initializeActionMethodValidators().
This check looks for a call to a parent method whose name is different than the method from which it is called. Consider the following code: class Daddy
{
protected function getFirstName()
{
return "Eidur";
}
protected function getSurName()
{
return "Gudjohnsen";
}
}
class Son
{
public function getFirstName()
{
return parent::getSurname();
}
}
The Loading history...
|
|||
| 317 | } |
||
| 318 | |||
| 319 | /** |
||
| 320 | * Used in unit testing. |
||
| 321 | * |
||
| 322 | * @param bool $flag |
||
| 323 | */ |
||
| 324 | public function setProtectedRequestMode($flag) |
||
| 325 | { |
||
| 326 | $this->protectedRequestMode = (bool)$flag; |
||
| 327 | } |
||
| 328 | |||
| 329 | /** |
||
| 330 | * @param Exception $exception |
||
| 331 | * @return string |
||
| 332 | */ |
||
| 333 | protected function getDebugMessageForException(Exception $exception) |
||
| 334 | { |
||
| 335 | return 'Debug mode – ' . $exception->getMessage(); |
||
| 336 | } |
||
| 337 | |||
| 338 | /** |
||
| 339 | * @return FormInterface |
||
| 340 | * @throws MissingArgumentException |
||
| 341 | */ |
||
| 342 | protected function getForm() |
||
| 343 | { |
||
| 344 | return $this->arguments->getArgument($this->formName)->getValue(); |
||
| 345 | } |
||
| 346 | |||
| 347 | /** |
||
| 348 | * @return FormObject |
||
| 349 | */ |
||
| 350 | protected function getFormObject() |
||
| 351 | { |
||
| 352 | /** @var FormObjectFactory $formObjectFactory */ |
||
| 353 | $formObjectFactory = Core::instantiate(FormObjectFactory::class); |
||
| 354 | |||
| 355 | return $formObjectFactory->getInstanceFromClassName($this->formClassName, $this->formName); |
||
| 356 | } |
||
| 357 | |||
| 358 | /** |
||
| 359 | * Wrapper for unit tests. |
||
| 360 | * |
||
| 361 | * @return Request |
||
| 362 | */ |
||
| 363 | protected function getRequest() |
||
| 364 | { |
||
| 365 | return $this->request; |
||
| 366 | } |
||
| 367 | } |
||
| 368 |
This check marks calls to methods that do not seem to exist on an object.
This is most likely the result of a method being renamed without all references to it being renamed likewise.