|
6 | 6 | use Drupal\Core\Entity\EntityRepositoryInterface;
|
7 | 7 | use Drupal\Core\Form\FormState;
|
8 | 8 | use Drupal\Core\Render\RendererInterface;
|
| 9 | +use Drupal\Core\Session\AccountInterface; |
9 | 10 | use Drupal\Core\TempStore\PrivateTempStoreFactory;
|
10 | 11 | use Symfony\Component\DependencyInjection\ContainerInterface;
|
11 | 12 | use Symfony\Component\HttpFoundation\JsonResponse;
|
12 | 13 | use Symfony\Component\HttpFoundation\Request;
|
| 14 | +use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; |
13 | 15 | use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
14 | 16 | use Drupal\Core\Ajax\AjaxResponse;
|
15 | 17 | use Drupal\Core\Entity\EntityInterface;
|
@@ -165,6 +167,32 @@ public function metadata(Request $request) {
|
165 | 167 | return new JsonResponse($metadata);
|
166 | 168 | }
|
167 | 169 |
|
| 170 | + /** |
| 171 | + * Throws an AccessDeniedHttpException if the request fails CSRF validation. |
| 172 | + * |
| 173 | + * This is used instead of \Drupal\Core\Access\CsrfAccessCheck, in order to |
| 174 | + * allow access for anonymous users. |
| 175 | + * |
| 176 | + * @todo Refactor this to an access checker. |
| 177 | + */ |
| 178 | + private static function checkCsrf(Request $request, AccountInterface $account) { |
| 179 | + $header = 'X-Drupal-Quickedit-CSRF-Token'; |
| 180 | + |
| 181 | + if (!$request->headers->has($header)) { |
| 182 | + throw new AccessDeniedHttpException(); |
| 183 | + } |
| 184 | + if ($account->isAnonymous()) { |
| 185 | + // For anonymous users, just the presence of the custom header is |
| 186 | + // sufficient protection. |
| 187 | + return; |
| 188 | + } |
| 189 | + // For authenticated users, validate the token value. |
| 190 | + $token = $request->headers->get($header); |
| 191 | + if (!\Drupal::csrfToken()->validate($token, $header)) { |
| 192 | + throw new AccessDeniedHttpException(); |
| 193 | + } |
| 194 | + } |
| 195 | + |
168 | 196 | /**
|
169 | 197 | * Returns AJAX commands to load in-place editors' attachments.
|
170 | 198 | *
|
@@ -315,6 +343,8 @@ protected function renderField(EntityInterface $entity, $field_name, $langcode,
|
315 | 343 | * The Ajax response.
|
316 | 344 | */
|
317 | 345 | public function entitySave(EntityInterface $entity) {
|
| 346 | + self::checkCsrf(\Drupal::request(), \Drupal::currentUser()); |
| 347 | + |
318 | 348 | // Take the entity from PrivateTempStore and save in entity storage.
|
319 | 349 | // fieldForm() ensures that the PrivateTempStore copy exists ahead.
|
320 | 350 | $tempstore = $this->tempStoreFactory->get('quickedit');
|
|
0 commit comments