Unverified Commit 7f148fe5 authored by Stephan Zeidler's avatar Stephan Zeidler
Browse files

Improve Gutenberg editor entity checks

parent 98765b4f
Loading
Loading
Loading
Loading
Loading
+24 −4
Changes for gutenberg.routing.yml: 24 added lines, 4 removed lines.
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@ gutenberg.media_edit_form:
      file:
        type: entity:media
  requirements:
    _entity_access: 'media.update'
    _permission: 'use gutenberg'

gutenberg.media.upload:
@@ -28,22 +29,26 @@ gutenberg.media.load:
  path: '/editor/media/load/{file}'
  defaults:
    _controller: '\Drupal\gutenberg\Controller\MediaController::load'
  options:
    parameters:
      file:
        type: entity:file
  methods: 'GET'
  requirements:
    _entity_access: 'file.view'
    _permission: 'use gutenberg'
    _format: json
gutenberg.media.edit:
  path: '/editor/media/edit/{file}'
  defaults:
    _controller: '\Drupal\gutenberg\Controller\MediaController::edit'
  options:
    parameters:
      file:
        type: entity:file
  methods: 'POST'
  requirements:
    _entity_access: 'file.update'
    _permission: 'use gutenberg'
    _format: json
    _csrf_request_header_token: 'TRUE'
@@ -77,11 +82,16 @@ gutenberg.media.render:
    _permission: 'use gutenberg'
    _format: json
gutenberg.entity.render:
  path: '/editor/entity/render/{entity_type}/{entity_id}/{view_mode}'
  path: '/editor/entity/render/{entity_type}/{entity}/{view_mode}'
  defaults:
    _controller: '\Drupal\gutenberg\Controller\EntityController::renderEntity'
  options:
    parameters:
      entity:
        type: entity:{entity_type}
  methods: 'GET'
  requirements:
    _entity_access: 'entity.view'
    _permission: 'use gutenberg'
    _format: json
gutenberg.entity.view_modes:
@@ -122,10 +132,15 @@ gutenberg.content_block_types.load:
    _permission: 'use gutenberg'
    _format: json
gutenberg.content_block.render:
  path: '/editor/content_block/render/{content_block_id}/{view_mode}'
  path: '/editor/content_block/render/{content_block}/{view_mode}'
  defaults:
    _controller: '\Drupal\gutenberg\Controller\BlocksController::renderContentBlock'
  options:
    parameters:
      content_block:
        type: entity:block_content
  requirements:
    _entity_access: 'content_block.view'
    _permission: 'use gutenberg'

gutenberg.content_block.clone_if_used:
@@ -225,7 +240,7 @@ gutenberg.pattern_categories.load_single:
        type: entity:taxonomy_term
  methods: 'GET'
  requirements:
    _custom_access: '\Drupal\gutenberg\Controller\ReusableBlocksController::access'
    _permission: 'use gutenberg'
    _format: json
gutenberg.pattern_categories.create:
  path: '/editor/patterns/categories'
@@ -262,11 +277,16 @@ gutenberg.media.search:
    _permission: 'use gutenberg'
    _format: json
gutenberg.media.update_data:
  path: '/editor/media/update_data/{fid}'
  path: '/editor/media/update_data/{file}'
  defaults:
    _controller: '\Drupal\gutenberg\Controller\MediaController::updateData'
  options:
    parameters:
      file:
        type: entity:file
  methods: 'POST'
  requirements:
    _entity_access: 'file.update'
    _permission: 'use gutenberg'
    _format: json
    _csrf_request_header_token: 'TRUE'
+34 −33
Changes for src/Controller/BlocksController.php: 34 added lines, 33 removed lines.
Original line number Diff line number Diff line
@@ -7,12 +7,14 @@ use Drupal\Core\Ajax\HtmlCommand;
use Drupal\Core\Block\BlockManagerInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Controller\ControllerBase;
use Drupal\block_content\BlockContentInterface;
use Drupal\Core\Render\Renderer;
use Drupal\gutenberg\BlocksRendererHelper;
use Drupal\gutenberg\GutenbergContentBlocksManager;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Drupal\Core\Plugin\Context\ContextRepositoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Theme\ThemeInitializationInterface;
@@ -273,36 +275,22 @@ class BlocksController extends ControllerBase {
   *
   * @param \Symfony\Component\HttpFoundation\Request $request
   *  The request.
   * @param string $content_block_id
   *  The content block id.
   * @param \Drupal\block_content\BlockContentInterface $content_block
   *  The content block.
   * @param string $view_mode
   *  The view mode.
   *
   * @return \Symfony\Component\HttpFoundation\JsonResponse
   *  The JSON response.
   */
  public function renderContentBlock(Request $request, $content_block_id, $view_mode = 'default') {
    $content_block = $this->entityTypeManager->getStorage('block_content')->load($content_block_id);
  public function renderContentBlock(Request $request, BlockContentInterface $content_block, $view_mode = 'default') {
    if (!$this->contentBlocksManager->isAllowedContentBlockType($content_block->bundle())) {
      throw new AccessDeniedHttpException();
    }

    $selector = '#content_block-' . $content_block_id . '-' . $view_mode;
    $selector = '#content_block-' . $content_block->id() . '-' . $view_mode;
    $response = new AjaxResponse();

    if (!$content_block) {
      $build = [
        '#theme' => 'status_messages',
        '#message_list' => [
          'warning' => [
            $this->t('Unable to render block. Check block settings or permissions.'),
          ],
        ],
        '#status_headings' => [
          'error' => $this->t('Warning Message'),
        ],
      ];
      $response->addCommand(new HtmlCommand($selector, $build));
      return $response;
    }

    $render = $this
      ->entityTypeManager
      ->getViewBuilder('block_content')
@@ -369,28 +357,40 @@ class BlocksController extends ControllerBase {
      $data = json_decode($request_content, TRUE);
    }

    if ($data['isDuplicate']) {
      // 'isDuplicate' means we already know this is a duplicate, so clone it.
      $cloned_block = $this->contentBlocksManager->cloneBlock($data['contentBlockId'], $data['entityId'], $data['entityType'], $data['entityBundle']);
      // return the cloned blocks id or 0 if it wasn't cloned
      $cloned_block_id = $cloned_block ? $cloned_block->id() : 0;
      return new JsonResponse(['id' => $cloned_block_id]);
    $content_block = $this->entityTypeManager->getStorage('block_content')->load($data['contentBlockId']);
    if ($content_block && (
      !$content_block->access('view') ||
      empty($data['entityBundle']) ||
      !$this->contentBlocksManager->isAllowedContentBlockType($content_block->bundle(), $data['entityBundle'])
    )) {
      throw new AccessDeniedHttpException();
    }
    else {

      $usage = $this->contentBlocksManager->getBlockUsage($data['contentBlockId']);
    $usage = $data['isDuplicate'] ? NULL : $this->contentBlocksManager->getBlockUsage($data['contentBlockId']);
    $should_clone = $data['isDuplicate'] || ($usage && $usage['entity_id'] != $data['entityId']);

    if ($should_clone) {
      if (!$this->currentUser()
        ->hasPermission('create and edit custom gutenberg content blocks')) {
        throw new AccessDeniedHttpException();
      }

      if ($data['entityId']) {
        $entity = $this->entityTypeManager->getStorage($data['entityType'])
          ->load($data['entityId']);
        if (!$entity || !$entity->access('update', $this->currentUser())) {
          throw new AccessDeniedHttpException();
        }
      }

      if ($usage && $usage['entity_id'] != $data['entityId']) {
      $cloned_block = $this->contentBlocksManager->cloneBlock($data['contentBlockId'], $data['entityId'], $data['entityType'], $data['entityBundle']);
      // return the cloned blocks id or 0 if it wasn't cloned
      $cloned_block_id = $cloned_block ? $cloned_block->id() : 0;
      return new JsonResponse(['id' => $cloned_block_id]);
    }

    }

    // we have no usage record yet, so it's ok to use if it exists.
    if ($content_block = $this->entityTypeManager->getStorage('block_content')->load($data['contentBlockId'])) {
    if ($content_block) {
      return new JsonResponse(['id' => $content_block->id()]);
    }

@@ -398,4 +398,5 @@ class BlocksController extends ControllerBase {
    return new JsonResponse(['id' => 0]);

  }

}
+4 −4
Changes for src/Controller/EntityController.php: 4 added lines, 4 removed lines.
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@ namespace Drupal\gutenberg\Controller;

use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Render\Renderer;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
@@ -97,16 +98,15 @@ class EntityController extends ControllerBase {
   *
   * @param \Symfony\Component\HttpFoundation\Request $request
   *  The request object.
   * @param integer $entity_id
   *  The entity id.
   * @param \Drupal\Core\Entity\EntityInterface $entity
   *  The entity.
   * @param string $entity_type
   *  The entity type.
   * @param string $view_mode
   *  The view mode.
   * @return \Symfony\Component\HttpFoundation\JsonResponse
   */
  public function renderEntity(Request $request, $entity_id, $entity_type, $view_mode = 'default') {
    $entity = $this->entityTypeManager->getStorage($entity_type)->load($entity_id);
  public function renderEntity(Request $request, EntityInterface $entity, $entity_type, $view_mode = 'default') {
    $entity_view = $this->entityTypeManager->getViewBuilder($entity_type)->view($entity, $view_mode);
    $entity_rendered = $this->renderer->renderRoot($entity_view);
    return new JsonResponse([
+14 −5
Changes for src/Controller/MediaController.php: 14 added lines, 5 removed lines.
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Drupal\editor\Entity\Editor;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;

@@ -149,7 +150,12 @@ class MediaController extends ControllerBase {
        throw new MediaEntityNotFoundException();
      }

      return new JsonResponse($this->mediaService->loadMediaData(reset($media_entities)));
      $media_entity = reset($media_entities);
      if (!$media_entity->access('view', $this->currentUser())) {
        throw new AccessDeniedHttpException();
      }

      return new JsonResponse($this->mediaService->loadMediaData($media_entity));
    }
    catch (MediaEntityNotFoundException $exception) {
      throw new NotFoundHttpException($exception->getMessage(), $exception);
@@ -174,6 +180,9 @@ class MediaController extends ControllerBase {
      }

      $media_entity = reset($media_entities);
      if (!$media_entity->access('view', $this->currentUser())) {
        throw new AccessDeniedHttpException();
      }

      return new JsonResponse([
        'view_modes' => $this->mediaService->getRenderedMediaEntity($media_entity),
@@ -355,15 +364,15 @@ class MediaController extends ControllerBase {
   *
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   Current request.
   * @param string|int $fid
   *   File id.
   * @param \Drupal\file\Entity\File $file
   *   File entity.
   *
   * @return \Symfony\Component\HttpFoundation\JsonResponse
   *   The JSON response.
   *
   * @throws \Symfony\Component\HttpKernel\Exception\HttpException
   */
  public function updateData(Request $request, $fid) {
  public function updateData(Request $request, File $file) {
    $data = json_decode($request->getContent(), TRUE);

    if (json_last_error() !== JSON_ERROR_NONE) {
@@ -371,7 +380,7 @@ class MediaController extends ControllerBase {
    }

    try {
      $this->mediaService->updateMediaData($fid, $data);
      $this->mediaService->updateMediaData($file->id(), $data);
    }
    catch (\Throwable $exception) {
      return new JsonResponse(['message' => 'Data could not be updated'], JsonResponse::HTTP_INTERNAL_SERVER_ERROR);
+8 −0
Changes for src/Form/ContentBlockSettingsForm.php: 8 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ use Drupal\Core\Entity\Entity\EntityFormDisplay;
use Drupal\Core\Entity\EntityDisplayRepositoryInterface;
use Drupal\Core\Form\SubformStateInterface;
use Drupal\Core\Session\AccountInterface;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;

/**
 * Provides a form to update a block.
@@ -150,6 +151,10 @@ class ContentBlockSettingsForm extends FormBase implements BaseFormIdInterface {
   *   The form array.
   */
  public function buildForm(array $form, FormStateInterface $form_state, $type = NULL, $block_id = NULL, $entity_type = 'node', $entity_id = NULL, $entity_bundle = NULL, $config = []) {
    if (!$type || !$this->contentBlocksManager->isAllowedContentBlockType($type, $entity_bundle)) {
      throw new AccessDeniedHttpException();
    }

    $form_state->set('block_theme', $this->config('system.theme')
      ->get('default'));
    $user_input = $form_state->getUserInput();
@@ -171,6 +176,9 @@ class ContentBlockSettingsForm extends FormBase implements BaseFormIdInterface {

      if ($block_id && is_numeric($block_id)) {
        $block = BlockContent::load($block_id);
        if ($block && $block->bundle() !== $type) {
          throw new AccessDeniedHttpException();
        }
      }

      // No block found, create a new one.
Loading