diff --git a/composer.json b/composer.json index ad92ec2bb..24b18a1f1 100644 --- a/composer.json +++ b/composer.json @@ -181,7 +181,8 @@ "Config install issues": "https://www.drupal.org/files/issues/2022-10-04/password_policy_field_last_password_reset_unknown_error_2771129-134.patch" }, "drupal/paragraphs": { - "Inline Paragraphs widget performance update": "./patches/paragraphs-improve-performance-2935134.patch" + "Inline Paragraphs widget performance update": "./patches/paragraphs-improve-performance-2935134.patch", + "Paragraph access check using incorrect revision of its parent https://www.drupal.org/project/paragraphs/issues/3090200#comment-14762651": "./patches/3084934-13_combine_3090200-22.patch" }, "drupal/simplesamlphp_auth": { "Masquerade and Logout Fix": "./patches/FOIA-78964-simplesamlphp-logout.patch" diff --git a/composer.lock b/composer.lock index 0e16e39c4..a02ce48a1 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b1395023225687146bccfdfec2a62d56", + "content-hash": "92e2cef9af7a45a898a8d1e4d92211e2", "packages": [ { "name": "acquia/blt", @@ -19704,6 +19704,7 @@ "drupal/business_rules": 20, "drupal/confirm_leave": 10, "drupal/dbug": 20, + "drupal/drupal-extension": 5, "drupal/entity_clone": 10, "drupal/feeds": 20, "drupal/feeds_ex": 10, @@ -19720,8 +19721,7 @@ "drupal/rules": 15, "drupal/schemata": 10, "drupal/simplesamlphp_auth": 5, - "drupal/typed_data": 10, - "drupal/drupal-extension": 5 + "drupal/typed_data": 10 }, "prefer-stable": true, "prefer-lowest": false, @@ -19729,7 +19729,7 @@ "php": ">=8.1", "ext-dom": "*" }, - "platform-dev": [], + "platform-dev": {}, "platform-overrides": { "php": "8.1" }, diff --git a/patches/3084934-13_combine_3090200-22.patch b/patches/3084934-13_combine_3090200-22.patch new file mode 100644 index 000000000..c03bee866 --- /dev/null +++ b/patches/3084934-13_combine_3090200-22.patch @@ -0,0 +1,97 @@ +diff --git a/src/ParagraphAccessControlHandler.php b/src/ParagraphAccessControlHandler.php +index 2946808..ba06541 100644 +--- a/src/ParagraphAccessControlHandler.php ++++ b/src/ParagraphAccessControlHandler.php +@@ -7,6 +7,8 @@ use Drupal\Core\Entity\EntityAccessControlHandler; + use Drupal\Core\Entity\EntityHandlerInterface; + use Drupal\Core\Entity\EntityInterface; + use Drupal\Core\Entity\EntityTypeInterface; ++use Drupal\Core\Entity\EntityTypeManagerInterface; ++use Drupal\Core\Entity\TranslatableRevisionableStorageInterface; + use Drupal\Core\Session\AccountInterface; + use Drupal\Core\Access\AccessResult; + use Symfony\Component\DependencyInjection\ContainerInterface; +@@ -25,6 +27,13 @@ class ParagraphAccessControlHandler extends EntityAccessControlHandler implement + */ + protected $configFactory; + ++ /** ++ * The entity type manager. ++ * ++ * @var \Drupal\Core\Entity\EntityTypeManagerInterface ++ */ ++ protected $entityTypeManager; ++ + /** + * Constructs a TranslatorAccessControlHandler object. + * +@@ -32,10 +41,13 @@ class ParagraphAccessControlHandler extends EntityAccessControlHandler implement + * The entity type definition. + * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory + * The config object factory. ++ * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager ++ * The entity type manager. + */ +- public function __construct(EntityTypeInterface $entity_type, ConfigFactoryInterface $config_factory) { ++ public function __construct(EntityTypeInterface $entity_type, ConfigFactoryInterface $config_factory, EntityTypeManagerInterface $entity_type_manager) { + parent::__construct($entity_type); + $this->configFactory = $config_factory; ++ $this->entityTypeManager = $entity_type_manager; + } + + /** +@@ -44,7 +56,8 @@ class ParagraphAccessControlHandler extends EntityAccessControlHandler implement + public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) { + return new static( + $entity_type, +- $container->get('config.factory') ++ $container->get('config.factory'), ++ $container->get('entity_type.manager') + ); + } + +@@ -61,15 +74,40 @@ class ParagraphAccessControlHandler extends EntityAccessControlHandler implement + else { + $access_result = AccessResult::allowed(); + } ++ $parent_entity = $paragraph->getParentEntity(); + if ($paragraph->getParentEntity() != NULL) { + // Delete permission on the paragraph, should just depend on 'update' + // access permissions on the parent. +- $operation = ($operation == 'delete') ? 'update' : $operation; ++ $operation = ($operation === 'delete') ? 'update' : $operation; + // Library items have no support for parent entity access checking. +- if ($paragraph->getParentEntity()->getEntityTypeId() != 'paragraphs_library_item') { +- $parent_access = $paragraph->getParentEntity()->access($operation, $account, TRUE); +- $access_result = $access_result->andIf($parent_access); ++ if ($parent_entity->getEntityTypeId() == 'paragraphs_library_item') { ++ return $access_result; ++ } ++ // Paragraphs on blocks and paragraphs can get into a state that makes it ++ // difficult to find the correct revision of the parent to check access ++ // against, so return the current access result in that case. ++ $skip_types = ['block_content', 'paragraph']; ++ if (($operation == 'view' || $operation == 'update') && in_array($parent_entity->getEntityTypeId(), $skip_types)) { ++ return $access_result; ++ } ++ if ($operation !== 'view') { ++ $storage = $this->entityTypeManager->getStorage($parent_entity->getEntityTypeId()); ++ // Load the latest revision if the parent entity is not new and is revisionable. ++ if ($storage instanceof TranslatableRevisionableStorageInterface && !$parent_entity->isNew()) { ++ $parent_entity_langcode = $parent_entity->language()->getId(); ++ $parent_entity_id = $storage->getLatestTranslationAffectedRevisionId($parent_entity->id(), ++ $parent_entity_langcode); ++ if ($parent_entity_id) { ++ $parent_entity = $storage->loadRevision($parent_entity_id); ++ if ($parent_entity && $parent_entity->hasTranslation($parent_entity_langcode)) { ++ $parent_entity = $parent_entity->getTranslation($parent_entity_langcode); ++ } ++ } ++ } + } ++ // Now check access on the paragraph's parent. ++ $parent_access = $parent_entity->access($operation, $account, TRUE); ++ $access_result = $access_result->andIf($parent_access); + } + return $access_result; + }