diff --git a/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php b/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php
index f3262a811a..7cf1082333 100644
--- a/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php
+++ b/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php
@@ -8,6 +8,7 @@
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Field\FieldTypePluginManagerInterface;
+use Drupal\Core\Session\AccountSwitcherInterface;
use Drupal\Core\TypedData\TranslatableInterface;
use Drupal\Core\TypedData\TypedDataInterface;
use Drupal\migrate\Audit\HighestIdInterface;
@@ -17,6 +18,7 @@
use Drupal\migrate\MigrateException;
use Drupal\migrate\Plugin\MigrateIdMapInterface;
use Drupal\migrate\Row;
+use Drupal\user\EntityOwnerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
@@ -102,6 +104,13 @@ class EntityContentBase extends Entity implements HighestIdInterface, MigrateVal
*/
protected $fieldTypeManager;
+ /**
+ * The account switcher service.
+ *
+ * @var \Drupal\Core\Session\AccountSwitcherInterface
+ */
+ protected $accountSwitcher;
+
/**
* Constructs a content entity.
*
@@ -133,7 +142,7 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration = NULL) {
$entity_type = static::getEntityTypeId($plugin_id);
- return new static(
+ $instance = new static(
$configuration,
$plugin_id,
$plugin_definition,
@@ -143,6 +152,8 @@ public static function create(ContainerInterface $container, array $configuratio
$container->get('entity_field.manager'),
$container->get('plugin.manager.field.field_type')
);
+ $instance->accountSwitcher = $container->get('account_switcher');
+ return $instance;
}
/**
@@ -184,8 +195,19 @@ public function isEntityValidationRequired(FieldableEntityInterface $entity) {
* {@inheritdoc}
*/
public function validateEntity(FieldableEntityInterface $entity) {
+ // Entity validation can require the user that owns the entity. Switch to
+ // use that user during validation.
+ $switch_user = (($entity instanceof EntityOwnerInterface) && $user = $entity->getOwner());
+ if ($switch_user) {
+ $this->getAccountSwitcher()->switchTo($user);
+ }
+
$violations = $entity->validate();
+ if ($switch_user) {
+ $this->getAccountSwitcher()->switchBack();
+ }
+
if (count($violations) > 0) {
throw new EntityValidationException($violations);
}
@@ -375,4 +397,17 @@ public function getHighestId() {
return (int) current($values);
}
+ /**
+ * Gets the account switcher service.
+ *
+ * @return \Drupal\Core\Session\AccountSwitcherInterface
+ * The account switcher service.
+ */
+ protected function getAccountSwitcher(): AccountSwitcherInterface {
+ if (!isset($this->accountSwitcher)) {
+ $this->accountSwitcher = \Drupal::service('account_switcher');
+ }
+ return $this->accountSwitcher;
+ }
+
}
diff --git a/core/modules/migrate/tests/src/Kernel/MigrateEntityContentValidationTest.php b/core/modules/migrate/tests/src/Kernel/MigrateEntityContentValidationTest.php
index c0cf029412..d34253b879 100644
--- a/core/modules/migrate/tests/src/Kernel/MigrateEntityContentValidationTest.php
+++ b/core/modules/migrate/tests/src/Kernel/MigrateEntityContentValidationTest.php
@@ -2,10 +2,15 @@
namespace Drupal\Tests\migrate\Kernel;
+use Drupal\field\Entity\FieldConfig;
+use Drupal\field\Entity\FieldStorageConfig;
+use Drupal\filter\Entity\FilterFormat;
use Drupal\KernelTests\KernelTestBase;
use Drupal\migrate\Event\MigrateEvents;
use Drupal\migrate\Event\MigrateIdMapMessageEvent;
use Drupal\migrate\MigrateExecutable;
+use Drupal\user\Entity\Role;
+use Drupal\user\Entity\User;
use Drupal\user\Plugin\Validation\Constraint\UserNameConstraint;
/**
@@ -18,7 +23,16 @@ class MigrateEntityContentValidationTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
- protected static $modules = ['migrate', 'system', 'user', 'entity_test'];
+ protected static $modules = [
+ 'entity_test',
+ 'field',
+ 'filter',
+ 'filter_test',
+ 'migrate',
+ 'system',
+ 'text',
+ 'user',
+ ];
/**
* Messages accumulated during the migration run.
@@ -33,9 +47,11 @@ class MigrateEntityContentValidationTest extends KernelTestBase {
protected function setUp(): void {
parent::setUp();
- $this->installConfig(['system', 'user']);
$this->installEntitySchema('user');
+ $this->installEntitySchema('user_role');
$this->installEntitySchema('entity_test');
+ $this->installSchema('system', ['sequences']);
+ $this->installConfig(['field', 'filter_test', 'system', 'user']);
$this->container
->get('event_dispatcher')
@@ -140,6 +156,96 @@ public function test2() {
$this->assertArrayNotHasKey(3, $this->messages, 'Fourth message should not exist.');
}
+ /**
+ * Tests validation for entities that are instances of EntityOwnerInterface.
+ */
+ public function testEntityOwnerValidation() {
+ // Filter formats have permissions tied to user, user roles and permissions.
+ /* @var \Drupal\filter\FilterFormatInterface $filter_test_format */
+ $filter_test_format = FilterFormat::load('filter_test');
+
+ // Create 2 users, an admin user who has filter permission and another who
+ // does not have said access.
+ /* @var \Drupal\user\RoleInterface $role */
+ $role = Role::create([
+ 'id' => 'admin',
+ 'label' => 'admin',
+ 'is_admin' => TRUE,
+ ]);
+ $role->grantPermission($filter_test_format->getPermissionName());
+ $role->save();
+ $admin_user = User::create([
+ 'name' => 'foobar',
+ 'mail' => 'foobar@example.com',
+ ]);
+ $admin_user->addRole($role->id());
+ $admin_user->save();
+ $normal_user = User::create([
+ 'name' => 'normal user',
+ 'mail' => 'normal@example.com',
+ ]);
+ $normal_user->save();
+
+ // Add a "body" field with the filter format.
+ $field_name = mb_strtolower($this->randomMachineName());
+ $field_storage = FieldStorageConfig::create([
+ 'field_name' => $field_name,
+ 'entity_type' => 'entity_test',
+ 'type' => 'text',
+ ]);
+ $field_storage->save();
+ FieldConfig::create([
+ 'field_storage' => $field_storage,
+ 'bundle' => 'entity_test',
+ ])->save();
+
+ // Attempt to migrate entities. One owned by the admin user, the other is a
+ // non-permissioned user.
+ $definition = [
+ 'source' => [
+ 'plugin' => 'embedded_data',
+ 'data_rows' => [
+ [
+ 'id' => 1,
+ 'uid' => $admin_user->id(),
+ 'body' => [
+ 'value' => 'foo',
+ 'format' => 'filter_test',
+ ],
+ ],
+ [
+ 'id' => 2,
+ 'uid' => $normal_user->id(),
+ 'body' => [
+ 'value' => 'bar',
+ 'format' => 'filter_test',
+ ],
+ ],
+ ],
+ 'ids' => [
+ 'id' => ['type' => 'integer'],
+ ],
+ ],
+ 'process' => [
+ 'id' => 'id',
+ 'user_id' => 'uid',
+ "$field_name/value" => 'body/value',
+ "$field_name/format" => 'body/format',
+ ],
+ 'destination' => [
+ 'plugin' => 'entity:entity_test',
+ 'validate' => TRUE,
+ ],
+ ];
+ $this->container->get('current_user')->setAccount($normal_user);
+ $this->runImport($definition);
+
+ // The second, non-permissioned user import should fail validation because
+ // they do not have access to use "filter_test" filter.
+ $this->assertSame(sprintf('2: [entity_test: 2]: user_id.0.target_id=This entity (user: %s) cannot be referenced.||%s.0.format=The value you selected is not a valid choice.', $normal_user->id(), $field_name), $this->messages[0], 'First message should have 2 validation errors.');
+ $this->assertArrayNotHasKey(1, $this->messages, 'Second message should not exist.');
+ }
+
/**
* Reacts to map message event.
*