Commit c0203de1 authored by Adam Bramley's avatar Adam Bramley Committed by Adam Bramley
Browse files

Issue #3117258 by dpi, acbramley, larowlan, Suresh Prabhu Parkala, Sam152:...

Issue #3117258 by dpi, acbramley, larowlan, Suresh Prabhu Parkala, Sam152: Preview link can return AccessResult::forbidden for entity reference fields rendered on the preview route
parent 348298e9
Loading
Loading
Loading
Loading
+20 −2
Original line number Diff line number Diff line
@@ -20,7 +20,9 @@ use Drupal\preview_link\Routing\PreviewLinkRouteProvider;
 * Implements hook_entity_type_alter().
 */
function preview_link_entity_type_alter(array &$entity_types): void {
  $supported_entity_types = array_filter($entity_types, [PreviewLinkUtility::class, 'isEntityTypeSupported']);
  $supported_entity_types = array_filter($entity_types, [
    PreviewLinkUtility::class, 'isEntityTypeSupported'
  ]);

  /** @var \Drupal\Core\Entity\ContentEntityType $type */
  foreach ($supported_entity_types as $type) {
@@ -68,10 +70,26 @@ function preview_link_entity_access(EntityInterface $entity, string $operation,
  $neutral = AccessResult::neutral()
    ->addCacheableDependency($entity)
    ->addCacheContexts(['preview_link_route']);

  if ($operation !== 'view' || !($entity instanceof ContentEntityInterface)) {
    return $neutral;
  }
  return \Drupal::service('access_check.preview_link')->access($entity, \Drupal::routeMatch()->getParameter('preview_token'));

  $routeMatch = \Drupal::routeMatch();
  $currentRoute = $routeMatch->getRouteObject();
  if (!$currentRoute) {
    // In cli contexts, there may be no route.
    return $neutral;
  }
  $entityParameterName = $currentRoute->getOption('preview_link.entity_type_id');
  $route_entity = $routeMatch->getParameter($entityParameterName);

  // Only run our access checks on the entity we're previewing.
  if ($route_entity instanceof ContentEntityInterface && $route_entity->id() === $entity->id() && $route_entity->getEntityTypeId() === $entity->getEntityTypeId()) {
    return \Drupal::service('access_check.preview_link')->access($entity, $routeMatch->getParameter('preview_token'));
  }

  return $neutral;
}

/**
+67 −0
Original line number Diff line number Diff line
@@ -5,7 +5,10 @@ declare(strict_types = 1);
namespace Drupal\Tests\preview_link\Functional;

use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\Entity\EntityViewDisplay;
use Drupal\Core\Url;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\preview_link\Entity\PreviewLink;
use Drupal\preview_link\Entity\PreviewLinkInterface;
use Drupal\Tests\BrowserTestBase;
@@ -157,6 +160,70 @@ class PreviewLinkAccessTest extends BrowserTestBase {
    $this->assertSession()->statusCodeEquals(200);
  }

  /**
   * Tests access for a referenced entity on a preview link route.
   */
  public function testPreviewLinkReferencedEntity() {
    // Set up an entity reference field.
    $field_storage = FieldStorageConfig::create([
      'field_name' => 'entity_test_rev_ref',
      'entity_type' => 'entity_test_rev',
      'type' => 'entity_reference',
      'settings' => [
        'target_type' => 'entity_test_rev',
      ],
    ]);
    $field_storage->save();
    $instance = FieldConfig::create([
      'field_storage' => $field_storage,
      'bundle' => 'entity_test_rev',
      'label' => $this->randomMachineName(),
    ]);
    $instance->save();

    // Set up the field display.
    EntityViewDisplay::create([
      'targetEntityType' => 'entity_test_rev',
      'bundle' => 'entity_test_rev',
      'mode' => 'default',
      'status' => TRUE,
    ])->setComponent('entity_test_rev_ref', [
      // Render the entity in full to trigger the "view" operation since
      // EntityTestAccessControlHandler has $viewLabelOperation set to TRUE.
      'type' => 'entity_reference_entity_view',
    ])->save();

    // Create test content.
    $reference = EntityTestRev::create();
    $reference->save();
    $referee = EntityTestRev::create([
      'entity_test_rev_ref' => $reference,
    ]);
    $referee->save();

    $account = $this->createUser([
      'view test entity',
    ]);
    $this->drupalLogin($account);

    $preview = $this->getNewPreviewLinkForEntity($referee);
    $token = $preview->getToken();

    // Check the referenced entity shows on the preview page.
    $url = Url::fromRoute('entity.entity_test_rev.preview_link', [
      'entity_test_rev' => $referee->id(),
      'preview_token' => $token,
    ]);
    $this->drupalGet($url);
    $this->assertSession()->pageTextContains($reference->label());

    // Check it still shows the referenced entity when it has a preview link
    // as well.
    $this->getNewPreviewLinkForEntity($reference);
    $this->drupalGet($url);
    $this->assertSession()->pageTextContains($reference->label());
  }

  /**
   * Get a saved preview link for an entity.
   *