Skip to content
Snippets Groups Projects
Verified Commit 247bd31e authored by Alex Pott's avatar Alex Pott
Browse files

Issue #2953111 by quietone, yogeshmpawar, ravi.shankar, benjifisher, alexpott,...

Issue #2953111 by quietone, yogeshmpawar, ravi.shankar, benjifisher, alexpott, mikelutz, catch, andypost, larowlan, danflanagan8: Only migrate role permissions that exist on the destination
parent c3a7a32c
Branches
Tags
38 merge requests!12227Issue #3181946 by jonmcl, mglaman,!7452Issue #1797438. HTML5 validation is preventing form submit and not fully...,!54479.5.x SF update,!5014Issue #3071143: Table Render Array Example Is Incorrect,!4868Issue #1428520: Improve menu parent link selection,!4289Issue #1344552 by marcingy, Niklas Fiekas, Ravi.J, aleevas, Eduardo Morales...,!4114Issue #2707291: Disable body-level scrolling when a dialog is open as a modal,!4100Issue #3249600: Add support for PHP 8.1 Enums as allowed values for list_* data types,!3630Issue #2815301 by Chi, DanielVeza, kostyashupenko, smustgrave: Allow to create...,!3600Issue #3344629: Passing null to parameter #1 ($haystack) of type string is deprecated,!3291Issue #3336463: Rewrite rules for gzipped CSS and JavaScript aggregates never match,!3102Issue #3164428 by DonAtt, longwave, sahil.goyal, Anchal_gupta, alexpott: Use...,!2853#3274419 Makes BaseFieldOverride inherit the internal property from the base field.,!2378Issue #2875033: Optimize joins and table selection in SQL entity query implementation,!2334Issue #3228209: Add hasRole() method to AccountInterface,!2074Issue #2707689: NodeForm::actions() checks for delete access on new entities,!2062Issue #3246454: Add weekly granularity to views date sort,!1591Issue #3199697: Add JSON:API Translation experimental module,!1484Exposed filters get values from URL when Ajax is on,!1255Issue #3238922: Refactor (if feasible) uses of the jQuery serialize function to use vanillaJS,!1254Issue #3238915: Refactor (if feasible) uses of the jQuery ready function to use VanillaJS,!1162Issue #3100350: Unable to save '/' root path alias,!1105Issue #3025039: New non translatable field on translatable content throws error,!1073issue #3191727: Focus states on mobile second level navigation items fixed,!10223132456: Fix issue where views instances are emptied before an ajax request is complete,!957Added throwing of InvalidPluginDefinitionException from getDefinition().,!925Issue #2339235: Remove taxonomy hard dependency on node module,!877Issue #2708101: Default value for link text is not saved,!873Issue #2875228: Site install not using batch API service,!872Draft: Issue #3221319: Race condition when creating menu links and editing content deletes menu links,!844Resolve #3036010 "Updaters",!712Issue #2909128: Autocomplete intermittent on Chrome Android,!617Issue #3043725: Provide a Entity Handler for user cancelation,!579Issue #2230909: Simple decimals fail to pass validation,!560Move callback classRemove outside of the loop,!555Issue #3202493,!485Sets the autocomplete attribute for username/password input field on login form.,!30Issue #3182188: Updates composer usage to point at ./vendor/bin/composer
......@@ -35,13 +35,15 @@ process:
- plugin: node_update_7008
- plugin: flatten
- plugin: filter_format_permission
# A special flag so we can migrate permissions that do not exist yet.
# @todo Remove in https://www.drupal.org/project/drupal/issues/2953111.
skip_missing_permission_deprecation:
plugin: default_value
default_value: true
destination:
plugin: entity:user_role
migration_dependencies:
required:
- d6_filter_format
optional:
- block_content_type
- contact_category
- d6_comment_type
- d6_node_type
- d6_taxonomy_vocabulary
- d6_taxonomy_vocabulary_translation
......@@ -33,13 +33,15 @@ process:
'edit own forum topics': 'edit own forum content'
- plugin: flatten
weight: weight
# A special flag so we can migrate permissions that do not exist yet.
# @todo Remove in https://www.drupal.org/project/drupal/issues/2953111.
skip_missing_permission_deprecation:
plugin: default_value
default_value: true
destination:
plugin: entity:user_role
migration_dependencies:
optional:
- block_content_type
- contact_category
- d7_comment_type
- d7_filter_format
- d7_node_type
- d7_shortcut_set
- d7_taxonomy_vocabulary
- d7_taxonomy_vocabulary_translation
......@@ -202,7 +202,7 @@ public function calculateDependencies() {
$permission_definitions = \Drupal::service('user.permissions')->getPermissions();
$valid_permissions = array_intersect($this->permissions, array_keys($permission_definitions));
$invalid_permissions = array_diff($this->permissions, $valid_permissions);
if (!empty($invalid_permissions) && !$this->get('skip_missing_permission_deprecation')) {
if (!empty($invalid_permissions)) {
throw new \RuntimeException('Adding non-existent permissions to a role is not allowed. The incorrect permissions are "' . implode('", "', $invalid_permissions) . '".');
}
foreach ($valid_permissions as $permission) {
......
<?php
namespace Drupal\user\Plugin\migrate\destination;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\migrate\Plugin\migrate\destination\EntityConfigBase;
use Drupal\migrate\Plugin\MigrationInterface;
use Drupal\migrate\Row;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a destination plugin for migrating user role entities.
*
* @MigrateDestination(
* id = "entity:user_role"
* )
*/
class EntityUserRole extends EntityConfigBase {
/**
* All permissions on the destination site.
*
* @var string[]
*/
protected $destinationPermissions = [];
/**
* Builds a user role entity destination.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\migrate\Plugin\MigrationInterface $migration
* The migration.
* @param \Drupal\Core\Entity\EntityStorageInterface $storage
* The storage for this entity type.
* @param array $bundles
* The list of bundles this entity type has.
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
* The language manager.
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The configuration factory.
* @param array $destination_permissions
* All available permissions.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EntityStorageInterface $storage, array $bundles, LanguageManagerInterface $language_manager, ConfigFactoryInterface $config_factory, array $destination_permissions) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $migration, $storage, $bundles, $language_manager, $config_factory);
$this->destinationPermissions = $destination_permissions;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration = NULL) {
$entity_type_id = static::getEntityTypeId($plugin_id);
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$migration,
$container->get('entity_type.manager')->getStorage($entity_type_id),
array_keys($container->get('entity_type.bundle.info')->getBundleInfo($entity_type_id)),
$container->get('language_manager'),
$container->get('config.factory'),
array_keys($container->get('user.permissions')->getPermissions()),
);
}
/**
* {@inheritdoc}
*/
public function import(Row $row, array $old_destination_id_values = []): array|bool {
$permissions = $row->getDestinationProperty('permissions') ?? [];
// Get permissions that do not exist on the destination.
$invalid_permissions = array_diff($permissions, $this->destinationPermissions);
if ($invalid_permissions) {
sort($invalid_permissions);
// Log the message in the migration message table.
$message = "Permission(s) '" . implode("', '", $invalid_permissions) . "' not found.";
$this->migration->getIdMap()
->saveMessage($row->getSourceIdValues(), $message, MigrationInterface::MESSAGE_WARNING);
}
$valid_permissions = array_intersect($permissions, $this->destinationPermissions);
$row->setDestinationProperty('permissions', $valid_permissions);
return parent::import($row, $old_destination_id_values);
}
}
<?php
namespace Drupal\Tests\user\Functional\Update;
use Drupal\FunctionalTests\Update\UpdatePathTestBase;
use Drupal\user\Entity\Role;
/**
* Tests user_post_update_update_roles_followup() upgrade path.
*
* @group Update
* @group legacy
*/
class UserUpdateRoleMigrateTest extends UpdatePathTestBase {
/**
* {@inheritdoc}
*/
protected function setDatabaseDumpFiles() {
$this->databaseDumpFiles = [
__DIR__ . '/../../../../../system/tests/fixtures/update/drupal-9.4.0.bare.standard.php.gz',
];
}
/**
* Tests that roles have only existing permissions.
*/
public function testRolePermissions() {
/** @var \Drupal\Core\Database\Connection $connection */
$connection = \Drupal::service('database');
// Edit the authenticated role to have a non-existent permission.
$authenticated = $connection->select('config')
->fields('config', ['data'])
->condition('collection', '')
->condition('name', 'user.role.authenticated')
->execute()
->fetchField();
$authenticated = unserialize($authenticated);
$authenticated['permissions'][] = 'does_not_exist';
$connection->update('config')
->fields([
'data' => serialize($authenticated),
])
->condition('collection', '')
->condition('name', 'user.role.authenticated')
->execute();
$authenticated = Role::load('authenticated');
$this->assertTrue($authenticated->hasPermission('does_not_exist'), 'Authenticated role has a permission that does not exist');
$this->runUpdates();
$this->assertSession()->pageTextContains('The role Authenticated user has had non-existent permissions removed. Check the logs for details.');
$authenticated = Role::load('authenticated');
$this->assertFalse($authenticated->hasPermission('does_not_exist'), 'Authenticated role does not have a permission that does not exist');
$this->drupalLogin($this->createUser(['access site reports']));
$this->drupalGet('admin/reports/dblog', ['query' => ['type[]' => 'update']]);
$this->clickLink('The role Authenticated user has had the following non-…');
$this->assertSession()->pageTextContains('The role Authenticated user has had the following non-existent permission(s) removed: does_not_exist.');
}
}
......@@ -2,6 +2,7 @@
namespace Drupal\Tests\user\Kernel\Migrate\d6;
use Drupal\migrate\Plugin\MigrationInterface;
use Drupal\user\Entity\Role;
use Drupal\user\RoleInterface;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
......@@ -19,7 +20,43 @@ class MigrateUserRoleTest extends MigrateDrupal6TestBase {
*/
protected function setUp(): void {
parent::setUp();
$this->executeMigrations(['d6_filter_format', 'd6_user_role']);
$this->startCollectingMessages();
}
/**
* Assert the logged migrate messages.
*
* @param string[][] $role_data
* An array of role data keyed by the destination role id. The role data
* contains the source role id, an array of valid permissions and an array
* of invalid permissions.
* @param \Drupal\migrate\Plugin\MigrateIdMapInterface $id_map
* The migration ID map plugin.
*/
public function assertMessages(array $role_data, MigrateIdMapInterface $id_map) {
foreach ($id_map->getMessages() as $message) {
$permissions = implode("', '", $role_data[$message->dest_id]['invalid']);
$expected_message = "Permission(s) '" . $permissions . "' not found.";
$this->assertSame($expected_message, $message->message);
$this->assertSame(MigrationInterface::MESSAGE_WARNING, (int) $message->level);
}
}
/**
* Asserts there are no duplicate roles.
*/
public function assertNoDuplicateRoles() {
$roles = [
'anonymous1',
'authenticated1',
'administrator1',
'migrate_test_role_11',
'migrate_test_role_21',
'migrate_test_role_3_that_is_longer_than_thirty_two_characters1',
'migrate_test_role_41',
];
$this->assertEmpty(Role::loadMultiple($roles));
}
/**
......@@ -40,7 +77,6 @@ protected function assertRole(string $id, array $permissions, int $lookupId, Mig
/** @var \Drupal\user\RoleInterface $role */
$role = Role::load($id);
$this->assertInstanceOf(RoleInterface::class, $role);
sort($permissions);
$this->assertSame($permissions, $role->getPermissions());
$this->assertSame([[$id]], $id_map->lookupDestinationIds(['rid' => $lookupId]));
}
......@@ -49,81 +85,217 @@ protected function assertRole(string $id, array $permissions, int $lookupId, Mig
* Helper function to test the migration of the user roles. The user roles
* will be re-imported and the tests here will be repeated.
*
* @param array $permissions
* Contains the valid and invalid permissions.
* @param \Drupal\migrate\Plugin\MigrateIdMapInterface $id_map
* The map table plugin.
*
* @internal
*/
protected function assertRoles(MigrateIdMapInterface $id_map): void {
// The permissions for each role are found in the two tables in the Drupal 6
// source database. One is the permission table and the other is the
// filter_format table.
$permissions = [
// From permission table.
'access content',
'migrate test anonymous permission',
// From filter_format tables.
'use text format filtered_html',
];
$this->assertRole('anonymous', $permissions, 1, $id_map);
$permissions = [
// From permission table.
'access comments',
'access content',
'migrate test authenticated permission',
'post comments',
'skip comment approval',
// From filter_format.
'use text format filtered_html',
];
$this->assertRole('authenticated', $permissions, 2, $id_map);
$permissions = [
// From permission table.
'migrate test role 1 test permission',
// From filter format.
'use text format full_html',
'use text format php_code',
];
$this->assertRole('migrate_test_role_1', $permissions, 3, $id_map);
$permissions = [
// From permission table.
'migrate test role 2 test permission',
'use PHP for settings',
'administer contact forms',
'skip comment approval',
'edit own blog content',
'edit any blog content',
'delete own blog content',
'delete any blog content',
'create forum content',
'delete any forum content',
'delete own forum content',
'edit any forum content',
'edit own forum content',
'administer nodes',
'access content overview',
// From filter format.
'use text format php_code',
];
$this->assertRole('migrate_test_role_2', $permissions, 4, $id_map);
protected function assertRoles(array $permissions, MigrateIdMapInterface $id_map): void {
foreach ($permissions as $rid => $datum) {
$this->assertRole($rid, $datum['valid'], $datum['rid'], $id_map);
}
}
// The only permission for this role is a filter format.
$permissions = ['use text format php_code'];
$this->assertRole('migrate_test_role_3_that_is_longer_than_thirty_two_characters', $permissions, 5, $id_map);
/**
* Data provider for user role migration tests.
*/
public function providerTestUserRole() {
return [
'filter only' => [
'modules' => [],
'migrations' => [
'd6_filter_format',
'd6_user_role',
],
'role_data' => [
'anonymous' => [
'rid' => '1',
'valid' => [
'access content',
'use text format filtered_html',
],
'invalid' => [
'migrate test anonymous permission',
],
],
'authenticated' => [
'rid' => '2',
'valid' => [
'access content',
'use text format filtered_html',
],
'invalid' => [
'access comments',
'migrate test authenticated permission',
'post comments',
'skip comment approval',
],
],
'migrate_test_role_1' => [
'rid' => '3',
'valid' => [
'use text format full_html',
'use text format php_code',
],
'invalid' => [
'migrate test role 1 test permission',
],
],
'migrate_test_role_2' => [
'rid' => '4',
'valid' => [
'access content overview',
'administer nodes',
'use text format php_code',
],
'invalid' => [
'administer contact forms',
'create forum content',
'delete any blog content',
'delete any forum content',
'delete own blog content',
'delete own forum content',
'edit any blog content',
'edit any forum content',
'edit own blog content',
'edit own forum content',
'migrate test role 2 test permission',
'skip comment approval',
'use PHP for settings',
],
],
'migrate_test_role_3_that_is_longer_than_thirty_two_characters' => [
'rid' => '5',
'valid' => [
'use text format php_code',
],
'invalid' => [],
],
],
],
'all dependent migrations' => [
'modules' => [
'block',
'block_content',
'comment',
'contact',
'config_translation',
'language',
'link',
'menu_ui',
'node',
'taxonomy',
'text',
],
'migrations' => [
'language',
'd6_comment_type',
'block_content_type',
'contact_category',
'd6_filter_format',
'd6_taxonomy_vocabulary',
'd6_taxonomy_vocabulary_translation',
'd6_user_role',
],
'role_data' => [
'anonymous' => [
'rid' => '1',
'valid' => [
'access content',
'use text format filtered_html',
],
'invalid' => [
'migrate test anonymous permission',
],
],
'authenticated' => [
'rid' => '2',
'valid' => [
'access comments',
'access content',
'post comments',
'skip comment approval',
'use text format filtered_html',
],
'invalid' => [
'migrate test authenticated permission',
],
],
'migrate_test_role_1' => [
'rid' => '3',
'valid' => [
'use text format full_html',
'use text format php_code',
],
'invalid' => [
'migrate test role 1 test permission',
],
],
'migrate_test_role_2' => [
'rid' => '4',
'valid' => [
'access content overview',
'administer contact forms',
'administer nodes',
'create forum content',
'delete any forum content',
'delete own forum content',
'edit any forum content',
'edit own forum content',
'skip comment approval',
'use text format php_code',
],
'invalid' => [
'delete any blog content',
'delete own blog content',
'edit any blog content',
'edit own blog content',
'migrate test role 2 test permission',
'use PHP for settings',
],
],
'migrate_test_role_3_that_is_longer_than_thirty_two_characters' => [
'rid' => '5',
'valid' => [
'use text format php_code',
],
'invalid' => [],
],
],
],
];
}
/**
* Tests user role migration.
*
* @param string[] $modules
* A list of modules to install.
* @param string[] $migrations
* A list of migrations to execute.
* @param string[][] $role_data
* An array of role data keyed by the destination role id. The role data
* contains the source role id, an array of valid permissions and an array
* of invalid permissions.
*
* @dataProvider providerTestUserRole()
*/
public function testUserRole() {
public function testUserRole(array $modules, array $migrations, array $role_data) {
if ($modules) {
// Install modules that have migrations that may provide permissions.
\Drupal::service('module_installer')->install($modules);
$this->installEntitySchema('block_content');
$this->installConfig(['block_content', 'comment']);
$this->migrateContentTypes();
}
$this->executeMigrations($migrations);
$id_map = $this->getMigration('d6_user_role')->getIdMap();
$this->assertRoles($id_map);
// Test there are no duplicated roles.
// After all the migrations are run, there are changes to the permissions.
$this->assertRoles($role_data, $id_map);
$roles = [
'anonymous1',
'authenticated1',
......@@ -134,6 +306,9 @@ public function testUserRole() {
];
$this->assertEmpty(Role::loadMultiple($roles));
$this->assertMessages($role_data, $id_map);
$this->assertSame(4, $id_map->messageCount());
// Remove the map row for the migrate_test_role_1 role and rerun the
// migration. This will re-import the migrate_test_role_1 role migration
// again.
......@@ -157,11 +332,10 @@ public function testUserRole() {
$this->executeMigration('d6_user_role');
// Test there are no duplicated roles.
$roles[] = 'migrate_test_role_41';
$this->assertEmpty(Role::loadMultiple($roles));
$this->assertNoDuplicateRoles();
// Test that the existing roles have not changed.
$this->assertRoles($id_map);
$this->assertRoles($role_data, $id_map);
// Test the migration of the new role, migrate_test_role_4.
$permissions = ['access content'];
......
......@@ -2,7 +2,7 @@
namespace Drupal\Tests\user\Kernel\Migrate\d7;
use Drupal\Core\Database\Database;
use Drupal\migrate\Plugin\MigrationInterface;
use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
use Drupal\user\Entity\Role;
use Drupal\user\RoleInterface;
......@@ -29,36 +29,46 @@ protected function setUp(): void {
* The role ID.
* @param string $label
* The role's expected label.
* @param int $original_rid
* The original (integer) ID of the role, to check permissions.
* @param string[] $permissions
* The expected permissions.
*
* @internal
*/
protected function assertEntity(string $id, string $label, int $original_rid): void {
protected function assertEntity(string $id, string $label, array $permissions): void {
/** @var \Drupal\user\RoleInterface $entity */
$entity = Role::load($id);
$this->assertInstanceOf(RoleInterface::class, $entity);
$this->assertSame($label, $entity->label());
if (isset($original_rid)) {
$permissions = Database::getConnection('default', 'migrate')
->select('role_permission', 'rp')
->fields('rp', ['permission'])
->condition('rid', $original_rid)
->execute()
->fetchCol();
sort($permissions);
$this->assertSame($permissions, $entity->getPermissions());
}
$this->assertSame($permissions, $entity->getPermissions());
}
/**
* Tests user role migration.
*/
public function testUserRole() {
$this->assertEntity('anonymous', 'anonymous user', 1);
$this->assertEntity('authenticated', 'authenticated user', 2);
$this->assertEntity('administrator', 'administrator', 3);
$anonymous_permissions = ['access content'];
$this->assertEntity('anonymous', 'anonymous user', $anonymous_permissions);
$this->assertEntity('authenticated', 'authenticated user', $anonymous_permissions);
$admin_permissions = [
'access administration pages',
'access content',
'access site in maintenance mode',
'access site reports',
'access user profiles',
'administer menu',
'administer modules',
'administer permissions',
'administer site configuration',
'administer software updates',
'administer themes',
'administer users',
'cancel account',
'change own username',
'select account cancellation method',
'view the administration theme',
];
$this->assertEntity('administrator', 'administrator', $admin_permissions);
// Test there are no duplicated roles.
$roles = [
'anonymous1',
......@@ -97,12 +107,107 @@ public function testUserRole() {
$this->assertEmpty(Role::loadMultiple($roles));
// Test that the existing roles have not changed.
$this->assertEntity('administrator', 'administrator', 3);
$this->assertEntity('anonymous', 'anonymous user', 1);
$this->assertEntity('authenticated', 'authenticated user', 2);
$this->assertEntity('administrator', 'administrator', $admin_permissions);
$this->assertEntity('anonymous', 'anonymous user', $anonymous_permissions);
$this->assertEntity('authenticated', 'authenticated user', $anonymous_permissions);
// Test the migration of the new role, test role.
$this->assertEntity('test_role', 'test role', 4);
$this->assertEntity('test_role', 'test role', $anonymous_permissions);
// Tests the migration log contains an error message.
// User role Authenticated.
$permissions[1] = [
'access comments',
'use text format filtered_html',
];
// User role test_role.
$permissions[2] = [
'access comments',
'post comments',
'skip comment approval',
'use text format custom_text_format',
'use text format filtered_html',
];
// User role administrator.
$permissions[3] = [
'access all views',
'access comments',
'access content overview',
'access contextual links',
'access dashboard',
'access news feeds',
'access overlay',
'access printer-friendly version',
'access site-wide contact form',
'access statistics',
'access toolbar',
'access user contact forms',
'add content to books',
'administer actions',
'administer blocks',
'administer book outlines',
'administer comments',
'administer contact forms',
'administer content types',
'administer filters',
'administer forums',
'administer image styles',
'administer languages',
'administer news feeds',
'administer nodes',
'administer search',
'administer shortcuts',
'administer statistics',
'administer taxonomy',
'administer unit tests',
'administer url aliases',
'administer views',
'block IP addresses',
'bypass node access',
'create article content',
'create new books',
'create page content',
'create url aliases',
'customize shortcut links',
'delete any article content',
'delete any page content',
'delete own article content',
'delete own page content',
'delete revisions',
'delete terms in 1',
'edit any article content',
'edit any page content',
'edit own article content',
'edit own comments',
'edit own page content',
'edit terms in 1',
'post comments',
'revert revisions',
'search content',
'skip comment approval',
'switch shortcut sets',
'translate admin strings',
'translate blocks',
'translate content',
'translate interface',
'translate user-defined strings',
'use PHP for settings',
'use advanced search',
'use text format custom_text_format',
'use text format filtered_html',
'use text format full_html',
'view own unpublished content',
'view post access counter',
'view revisions',
];
foreach ($id_map->getMessages() as $message) {
$expected_permissions = implode("', '", $permissions[$message->src_rid]);
$expected_message = "Permission(s) '" . $expected_permissions . "' not found.";
$this->assertSame($expected_message, $message->message);
$this->assertSame(MigrationInterface::MESSAGE_WARNING, (int) $message->level);
}
$this->assertSame(3, $id_map->messageCount());
}
}
......@@ -5,6 +5,10 @@
* Post update functions for User module.
*/
use Drupal\Core\Config\Entity\ConfigEntityUpdater;
use Drupal\Core\StringTranslation\PluralTranslatableMarkup;
use Drupal\user\Entity\Role;
/**
* Implements hook_removed_post_updates().
*/
......@@ -14,3 +18,39 @@ function user_removed_post_updates() {
'user_post_update_update_roles' => '10.0.0',
];
}
/**
* Remove non-existent permissions created by migrations.
*/
function user_post_update_update_migrated_roles_followup(&$sandbox = NULL) {
$cleaned_roles = [];
$existing_permissions = array_keys(\Drupal::service('user.permissions')
->getPermissions());
\Drupal::classResolver(ConfigEntityUpdater::class)
->update($sandbox, 'user_role', function (Role $role) use ($existing_permissions, &$cleaned_roles) {
$removed_permissions = array_diff($role->getPermissions(), $existing_permissions);
if (!empty($removed_permissions)) {
$cleaned_roles[] = $role->label();
\Drupal::logger('update')->notice(
'The role %role has had the following non-existent permission(s) removed: %permissions.',
[
'%role' => $role->label(),
'%permissions' => implode(', ', $removed_permissions),
]
);
$permissions = array_intersect($role->getPermissions(), $existing_permissions);
$role->set('permissions', $permissions);
return TRUE;
}
});
if (!empty($cleaned_roles)) {
return new PluralTranslatableMarkup(
count($cleaned_roles),
'The role %role_list has had non-existent permissions removed. Check the logs for details.',
'The roles %role_list have had non-existent permissions removed. Check the logs for details.',
['%role_list' => implode(', ', $cleaned_roles)]
);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment