diff --git a/core/modules/content_moderation/src/Permissions.php b/core/modules/content_moderation/src/Permissions.php
index a6d6ce5..038d6d9 100644
--- a/core/modules/content_moderation/src/Permissions.php
+++ b/core/modules/content_moderation/src/Permissions.php
@@ -30,6 +30,9 @@ public function transitionPermissions() {
'%workflow' => $workflow->label(),
'%transition' => $transition->label(),
]),
+ 'dependencies' => [
+ $workflow->getConfigDependencyKey() => [$workflow->getConfigDependencyName()],
+ ],
];
}
}
diff --git a/core/modules/content_moderation/tests/src/Kernel/ContentModerationPermissionsTest.php b/core/modules/content_moderation/tests/src/Kernel/ContentModerationPermissionsTest.php
index f17e4ac..8a41a5c 100644
--- a/core/modules/content_moderation/tests/src/Kernel/ContentModerationPermissionsTest.php
+++ b/core/modules/content_moderation/tests/src/Kernel/ContentModerationPermissionsTest.php
@@ -59,9 +59,19 @@ public function permissionsTestCases() {
[
'use simple_workflow transition publish' => [
'title' => 'Simple Workflow workflow: Use Publish transition.',
+ 'dependencies' => [
+ 'config' => [
+ 'workflows.workflow.simple_workflow'
+ ]
+ ],
],
'use simple_workflow transition create_new_draft' => [
'title' => 'Simple Workflow workflow: Use Create New Draft transition.',
+ 'dependencies' => [
+ 'config' => [
+ 'workflows.workflow.simple_workflow'
+ ]
+ ],
],
],
],
diff --git a/core/modules/content_translation/src/ContentTranslationPermissions.php b/core/modules/content_translation/src/ContentTranslationPermissions.php
index dc7959b..4ecc20e 100644
--- a/core/modules/content_translation/src/ContentTranslationPermissions.php
+++ b/core/modules/content_translation/src/ContentTranslationPermissions.php
@@ -72,6 +72,9 @@ public function contentPermissions() {
$permission["translate $bundle $entity_type_id"] = [
'title' => $this->t('Translate %bundle_label @entity_label', $t_args),
];
+ if (($bundle_entity_type = $entity_type->getBundleEntityType()) && $bundle_entity = $this->entityManager->getStorage($bundle_entity_type)->load($bundle)) {
+ $permission["translate $bundle $entity_type_id"]['dependencies'][$bundle_entity->getConfigDependencyKey()][] = $bundle_entity->getConfigDependencyName();
+ }
}
}
break;
@@ -80,6 +83,7 @@ public function contentPermissions() {
if ($this->contentTranslationManager->isEnabled($entity_type_id)) {
$permission["translate $entity_type_id"] = [
'title' => $this->t('Translate @entity_label', $t_args),
+ 'dependencies' => ['module' => [$entity_type->getProvider()]],
];
}
break;
diff --git a/core/modules/content_translation/tests/src/Kernel/ContentTranslationPermissionsTest.php b/core/modules/content_translation/tests/src/Kernel/ContentTranslationPermissionsTest.php
new file mode 100644
index 0000000..bc94223
--- /dev/null
+++ b/core/modules/content_translation/tests/src/Kernel/ContentTranslationPermissionsTest.php
@@ -0,0 +1,50 @@
+installEntitySchema('entity_test_mul');
+ $this->installEntitySchema('entity_test_mul_with_bundle');
+ EntityTestMulBundle::create([
+ 'id' => 'test',
+ 'label' => 'Test label',
+ 'description' => 'My test description',
+ ])->save();
+
+ }
+
+ /**
+ * Tests that enabling translation via the API triggers schema updates.
+ */
+ public function testPermissions() {
+ $this->container->get('content_translation.manager')->setEnabled('entity_test_mul', 'entity_test_mul', TRUE);
+ $this->container->get('content_translation.manager')->setEnabled('entity_test_mul_with_bundle', 'test', TRUE);
+ $permissions = \Drupal::service('user.permissions')->getPermissions();
+ $this->assertEquals(['entity_test'], $permissions['translate entity_test_mul']['dependencies']['module']);
+ $this->assertEquals(['entity_test.entity_test_mul_bundle.test'], $permissions['translate test entity_test_mul_with_bundle']['dependencies']['config']);
+ }
+
+}
diff --git a/core/modules/field_ui/src/FieldUiPermissions.php b/core/modules/field_ui/src/FieldUiPermissions.php
index be73db6..b36124c 100644
--- a/core/modules/field_ui/src/FieldUiPermissions.php
+++ b/core/modules/field_ui/src/FieldUiPermissions.php
@@ -48,17 +48,22 @@ public function fieldPermissions() {
foreach ($this->entityManager->getDefinitions() as $entity_type_id => $entity_type) {
if ($entity_type->get('field_ui_base_route')) {
+ // The permissions should depend on the module that provides the entity.
+ $dependencies = ['module' => [$entity_type->getProvider()]];
// Create a permission for each fieldable entity to manage
// the fields and the display.
$permissions['administer ' . $entity_type_id . ' fields'] = [
'title' => $this->t('%entity_label: Administer fields', ['%entity_label' => $entity_type->getLabel()]),
'restrict access' => TRUE,
+ 'dependencies' => $dependencies,
];
$permissions['administer ' . $entity_type_id . ' form display'] = [
- 'title' => $this->t('%entity_label: Administer form display', ['%entity_label' => $entity_type->getLabel()])
+ 'title' => $this->t('%entity_label: Administer form display', ['%entity_label' => $entity_type->getLabel()]),
+ 'dependencies' => $dependencies,
];
$permissions['administer ' . $entity_type_id . ' display'] = [
- 'title' => $this->t('%entity_label: Administer display', ['%entity_label' => $entity_type->getLabel()])
+ 'title' => $this->t('%entity_label: Administer display', ['%entity_label' => $entity_type->getLabel()]),
+ 'dependencies' => $dependencies,
];
}
}
diff --git a/core/modules/filter/src/FilterPermissions.php b/core/modules/filter/src/FilterPermissions.php
index fc22f3c..181394d 100644
--- a/core/modules/filter/src/FilterPermissions.php
+++ b/core/modules/filter/src/FilterPermissions.php
@@ -59,6 +59,13 @@ public function permissions() {
'#markup' => $this->t('Warning: This permission may have security implications depending on how the text format is configured.'),
'#suffix' => ''
],
+ // This permission is generated in behalf of $format text format, thus
+ // we add this text format as a config dependency.
+ 'dependencies' => [
+ $format->getConfigDependencyKey() => [
+ $format->getConfigDependencyName(),
+ ],
+ ],
];
}
}
diff --git a/core/modules/node/src/NodePermissions.php b/core/modules/node/src/NodePermissions.php
index 1996360..bf4a246 100644
--- a/core/modules/node/src/NodePermissions.php
+++ b/core/modules/node/src/NodePermissions.php
@@ -23,9 +23,20 @@ class NodePermissions {
*/
public function nodeTypePermissions() {
$perms = [];
+ /* @var \Drupal\node\Entity\NodeType $type */
// Generate node permissions for all node types.
foreach (NodeType::loadMultiple() as $type) {
- $perms += $this->buildPermissions($type);
+ $perms_to_add = array_map(
+ function ($perm) use ($type) {
+ // This permission is generated in behalf of a node type, therefore
+ // add the node type as a config dependency.
+ $perm['dependencies'] = [
+ $type->getConfigDependencyKey() => [$type->getConfigDependencyName()],
+ ];
+ return $perm;
+ },
+ $this->buildPermissions($type));
+ $perms += $perms_to_add;
}
return $perms;
diff --git a/core/modules/rest/src/RestPermissions.php b/core/modules/rest/src/RestPermissions.php
index 473c192..045e8c9 100644
--- a/core/modules/rest/src/RestPermissions.php
+++ b/core/modules/rest/src/RestPermissions.php
@@ -57,7 +57,17 @@ public function permissions() {
$resource_configs = $this->resourceConfigStorage->loadMultiple();
foreach ($resource_configs as $resource_config) {
$plugin = $resource_config->getResourcePlugin();
- $permissions = array_merge($permissions, $plugin->permissions());
+ // @todo work out what to do here for permission dependencies.
+ $permissions_to_add = array_map(function ($permission_info) use ($resource_config) {
+ $permission_info += ['dependencies' => []];
+ $permission_info['dependencies'] += [
+ $resource_config->getConfigDependencyKey() => [
+ $resource_config->getConfigDependencyName(),
+ ],
+ ];
+ return $permission_info;
+ }, $plugin->permissions());
+ $permissions = array_merge($permissions, $permissions_to_add);
}
return $permissions;
}
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Action/ActionResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/Action/ActionResourceTestBase.php
index 76aff4b..c2befa4 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/Action/ActionResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/Action/ActionResourceTestBase.php
@@ -11,7 +11,7 @@
/**
* {@inheritdoc}
*/
- public static $modules = ['user'];
+ public static $modules = ['action', 'user'];
/**
* {@inheritdoc}
@@ -73,15 +73,6 @@ protected function getExpectedNormalizedEntity() {
/**
* {@inheritdoc}
*/
- protected function getExpectedCacheContexts() {
- return [
- 'user.permissions',
- ];
- }
-
- /**
- * {@inheritdoc}
- */
protected function getNormalizedPostEntity() {
// @todo Update in https://www.drupal.org/node/2300677.
}
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/BaseFieldOverride/BaseFieldOverrideResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/BaseFieldOverride/BaseFieldOverrideResourceTestBase.php
index 1f4b03a..26eae27 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/BaseFieldOverride/BaseFieldOverrideResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/BaseFieldOverride/BaseFieldOverrideResourceTestBase.php
@@ -11,7 +11,7 @@
/**
* {@inheritdoc}
*/
- public static $modules = ['field', 'node'];
+ public static $modules = ['field', 'node', 'field_ui'];
/**
* {@inheritdoc}
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/EntityFormDisplay/EntityFormDisplayResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/EntityFormDisplay/EntityFormDisplayResourceTestBase.php
index cc4cf58..34cc290 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/EntityFormDisplay/EntityFormDisplayResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/EntityFormDisplay/EntityFormDisplayResourceTestBase.php
@@ -11,7 +11,7 @@
/**
* {@inheritdoc}
*/
- public static $modules = ['node'];
+ public static $modules = ['node', 'field_ui'];
/**
* {@inheritdoc}
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php
index 6faf028..5987d45 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php
@@ -453,12 +453,17 @@ public function testGet() {
if ($this->entity->getEntityType()->getLinkTemplates()) {
$this->assertArrayHasKey('Link', $response->getHeaders());
$link_relation_type_manager = $this->container->get('plugin.manager.link_relation_type');
+ // Filter out definitions that do not have a plugin.
+ // @see \Drupal\rest\Plugin\rest\resource\EntityResource::addLinkHeaders()
+ $link_relations = array_values(array_filter(array_keys($this->entity->getEntityType()->getLinkTemplates()), function ($relation_name) use ($link_relation_type_manager) {
+ return $link_relation_type_manager->hasDefinition($relation_name);
+ }));
$expected_link_relation_headers = array_map(function ($relation_name) use ($link_relation_type_manager) {
$link_relation_type = $link_relation_type_manager->createInstance($relation_name);
return $link_relation_type->isRegistered()
? $link_relation_type->getRegisteredName()
: $link_relation_type->getExtensionUri();
- }, array_keys($this->entity->getEntityType()->getLinkTemplates()));
+ }, $link_relations);
$parse_rel_from_link_header = function ($value) use ($link_relation_type_manager) {
$matches = [];
if (preg_match('/rel="([^"]+)"/', $value, $matches) === 1) {
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/EntityViewDisplay/EntityViewDisplayResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/EntityViewDisplay/EntityViewDisplayResourceTestBase.php
index ccc3aad..5234703 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/EntityViewDisplay/EntityViewDisplayResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/EntityViewDisplay/EntityViewDisplayResourceTestBase.php
@@ -11,7 +11,7 @@
/**
* {@inheritdoc}
*/
- public static $modules = ['node'];
+ public static $modules = ['node', 'field_ui'];
/**
* {@inheritdoc}
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/FieldConfig/FieldConfigResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/FieldConfig/FieldConfigResourceTestBase.php
index 05c05f4..89ddd93 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/FieldConfig/FieldConfigResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/FieldConfig/FieldConfigResourceTestBase.php
@@ -12,7 +12,7 @@
/**
* {@inheritdoc}
*/
- public static $modules = ['field', 'node'];
+ public static $modules = ['field', 'node', 'field_ui'];
/**
* {@inheritdoc}
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/FieldStorageConfig/FieldStorageConfigResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/FieldStorageConfig/FieldStorageConfigResourceTestBase.php
index aafc253..9eb5a32 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/FieldStorageConfig/FieldStorageConfigResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/FieldStorageConfig/FieldStorageConfigResourceTestBase.php
@@ -10,7 +10,7 @@
/**
* {@inheritdoc}
*/
- public static $modules = ['node'];
+ public static $modules = ['node', 'field_ui'];
/**
* {@inheritdoc}
@@ -92,13 +92,4 @@ protected function getExpectedUnauthorizedAccessMessage($method) {
}
}
- /**
- * {@inheritdoc}
- */
- protected function getExpectedCacheContexts() {
- return [
- 'user.permissions',
- ];
- }
-
}
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/View/ViewResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/View/ViewResourceTestBase.php
index 26ec7bb..58c2dbc 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/View/ViewResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/View/ViewResourceTestBase.php
@@ -10,7 +10,7 @@
/**
* {@inheritdoc}
*/
- public static $modules = ['views'];
+ public static $modules = ['views', 'views_ui'];
/**
* {@inheritdoc}
@@ -87,13 +87,4 @@ protected function getNormalizedPostEntity() {
// @todo Update in https://www.drupal.org/node/2300677.
}
- /**
- * {@inheritdoc}
- */
- protected function getExpectedCacheContexts() {
- return [
- 'user.permissions',
- ];
- }
-
}
diff --git a/core/modules/system/tests/modules/entity_test/config/schema/entity_test.schema.yml b/core/modules/system/tests/modules/entity_test/config/schema/entity_test.schema.yml
index d34d949..31aa1d6 100644
--- a/core/modules/system/tests/modules/entity_test/config/schema/entity_test.schema.yml
+++ b/core/modules/system/tests/modules/entity_test/config/schema/entity_test.schema.yml
@@ -27,3 +27,7 @@ entity_test.entity_test_bundle.*:
description:
type: text
label: 'Description'
+
+entity_test.entity_test_mul_bundle.*:
+ type: entity_test.entity_test_bundle.*
+ label: 'Entity test mul bundle'
diff --git a/core/modules/system/tests/modules/entity_test/entity_test.module b/core/modules/system/tests/modules/entity_test/entity_test.module
index 025cb5f..d595d4a 100644
--- a/core/modules/system/tests/modules/entity_test/entity_test.module
+++ b/core/modules/system/tests/modules/entity_test/entity_test.module
@@ -71,6 +71,7 @@ function entity_test_entity_types($filter = NULL) {
$types[] = 'entity_test_base_field_display';
$types[] = 'entity_test_string_id';
$types[] = 'entity_test_no_id';
+ $types[] = 'entity_test_mul_with_bundle';
}
$types[] = 'entity_test_mulrev';
$types[] = 'entity_test_mulrev_changed';
@@ -199,7 +200,7 @@ function entity_test_entity_bundle_info() {
$bundles = [];
$entity_types = \Drupal::entityManager()->getDefinitions();
foreach ($entity_types as $entity_type_id => $entity_type) {
- if ($entity_type->getProvider() == 'entity_test' && $entity_type_id != 'entity_test_with_bundle') {
+ if ($entity_type->getProvider() == 'entity_test' && !in_array($entity_type_id, ['entity_test_with_bundle', 'entity_test_mul_with_bundle'], TRUE)) {
$bundles[$entity_type_id] = \Drupal::state()->get($entity_type_id . '.bundles') ?: [$entity_type_id => ['label' => 'Entity Test Bundle']];
}
}
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulBundle.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulBundle.php
new file mode 100644
index 0000000..65e58b7
--- /dev/null
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulBundle.php
@@ -0,0 +1,77 @@
+description;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setDescription($description) {
+ $this->description = $description;
+ return $this;
+ }
+
+}
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulWithBundle.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulWithBundle.php
new file mode 100644
index 0000000..fb7e0d9
--- /dev/null
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulWithBundle.php
@@ -0,0 +1,49 @@
+drupalPostForm('admin/modules/uninstall', $edit, t('Uninstall'));
$this->assertText(\Drupal::translation()->translate('Configuration deletions'), 'Configuration deletions listed on the module install confirmation page.');
- $this->assertNoText(\Drupal::translation()->translate('Configuration updates'), 'No configuration updates listed on the module install confirmation page.');
+ $this->assertText(\Drupal::translation()->translate('Configuration updates'), 'Configuration updates listed on the module install confirmation page.');
$entity_types = [];
foreach ($node_dependencies as $entity) {
diff --git a/core/modules/taxonomy/src/TaxonomyPermissions.php b/core/modules/taxonomy/src/TaxonomyPermissions.php
index 1772a34..5321d23 100644
--- a/core/modules/taxonomy/src/TaxonomyPermissions.php
+++ b/core/modules/taxonomy/src/TaxonomyPermissions.php
@@ -50,7 +50,17 @@ public static function create(ContainerInterface $container) {
public function permissions() {
$permissions = [];
foreach (Vocabulary::loadMultiple() as $vocabulary) {
- $permissions += $this->buildPermissions($vocabulary);
+ $perms_to_add = array_map(
+ function ($perm) use ($vocabulary) {
+ // This permission is generated in behalf of a vocabulary, therefore
+ // add the vocabulary as a config dependency.
+ $perm['dependencies'] = [
+ $vocabulary->getConfigDependencyKey() => [$vocabulary->getConfigDependencyName()],
+ ];
+ return $perm;
+ },
+ $this->buildPermissions($vocabulary));
+ $permissions += $perms_to_add;
}
return $permissions;
}
diff --git a/core/modules/toolbar/tests/src/Functional/ToolbarAdminMenuTest.php b/core/modules/toolbar/tests/src/Functional/ToolbarAdminMenuTest.php
index 9585c86..02d85af 100644
--- a/core/modules/toolbar/tests/src/Functional/ToolbarAdminMenuTest.php
+++ b/core/modules/toolbar/tests/src/Functional/ToolbarAdminMenuTest.php
@@ -7,6 +7,7 @@
use Drupal\Core\Url;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\Tests\BrowserTestBase;
+use Drupal\user\Entity\Role;
use Drupal\user\RoleInterface;
/**
@@ -99,6 +100,14 @@ protected function setUp() {
* implementations.
*/
public function testModuleStatusChangeSubtreesHashCacheClear() {
+ // Give the user an admin role so permissions change when modules are
+ // installed and uninstalled.
+ $role = Role::load($this->createRole([]));
+ $role->setIsAdmin(TRUE);
+ $role->save();
+ $this->adminUser->addRole($role->id());
+ $this->adminUser->save();
+
// Uninstall a module.
$edit = [];
$edit['uninstall[taxonomy]'] = TRUE;
diff --git a/core/modules/user/src/Entity/Role.php b/core/modules/user/src/Entity/Role.php
index 0226f94..582fa35 100644
--- a/core/modules/user/src/Entity/Role.php
+++ b/core/modules/user/src/Entity/Role.php
@@ -186,4 +186,68 @@ public function preSave(EntityStorageInterface $storage) {
}
}
+ /**
+ * {@inheritdoc}
+ */
+ public function calculateDependencies() {
+ parent::calculateDependencies();
+ // Load all permissions.
+ $permissions = \Drupal::service('user.permissions')->getPermissions();
+ foreach ($this->permissions as $permission) {
+ // @todo Why is this necessary? Surely for a permission to be on role it
+ // has to exist.
+ if (isset($permissions[$permission])) {
+ // Depend on the module that is providing this permissions.
+ $this->addDependency('module', $permissions[$permission]['provider']);
+ // Depend on any other dependencies defined by permissions granted to
+ // this role.
+ if (!empty($permissions[$permission]['dependencies'])) {
+ foreach ($permissions[$permission]['dependencies'] as $type => $dependencies) {
+ foreach ($dependencies as $dependency) {
+ $this->addDependency($type, $dependency);
+ }
+ }
+ }
+ }
+ }
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function onDependencyRemoval(array $dependencies) {
+ $changed = parent::onDependencyRemoval($dependencies);
+ // Load all permission definitions.
+ $permission_definitions = \Drupal::service('user.permissions')->getPermissions();
+
+ // Convert dependencies to a list of names to make it easy to check.
+ foreach ($dependencies as $type => $items) {
+ foreach ($items as $key => $dependency) {
+ // Get the dependency name, based on dependency type.
+ $name = in_array($type, ['config', 'content']) ? $dependency->getConfigDependencyName() : $dependency;
+ $dependencies[$type][$key] = $name;
+ }
+ }
+
+ // If the permission is dependent on anything being deleted or uninstalled
+ // then remove the permission from the role.
+ foreach ($this->permissions as $key => $permission) {
+ if (in_array($permission_definitions[$permission]['provider'], $dependencies['module'], TRUE)) {
+ unset($this->permissions[$key]);
+ $changed = TRUE;
+ }
+ elseif (isset($permission_definitions[$permission]['dependencies'])) {
+ foreach ($permission_definitions[$permission]['dependencies'] as $type => $list) {
+ if (array_intersect($list, $dependencies[$type])) {
+ unset($this->permissions[$key]);
+ $changed = TRUE;
+ }
+ }
+ }
+ }
+
+ return $changed;
+ }
+
}
diff --git a/core/modules/user/src/PermissionHandler.php b/core/modules/user/src/PermissionHandler.php
index 1f07b25..3855944 100644
--- a/core/modules/user/src/PermissionHandler.php
+++ b/core/modules/user/src/PermissionHandler.php
@@ -33,6 +33,11 @@
* # (optional) Boolean, when set to true a warning about site security will
* # be displayed on the Permissions page. Defaults to false.
* restrict access: false
+ * # (optional) Dependency array following the same structure as the return
+ * # config entities dependencies.
+ * dependencies:
+ * config:
+ * - node.type.article
*
* # An array of callables used to generate dynamic permissions.
* permission_callbacks:
@@ -41,6 +46,7 @@
* - Drupal\filter\FilterPermissions::permissions
* @endcode
*
+ * @see \Drupal\user\PermissionHandlerInterface::getPermissions()
* @see filter.permissions.yml
* @see \Drupal\filter\FilterPermissions
* @see user_api
@@ -130,10 +136,10 @@ public function moduleProvidesPermissions($module_name) {
* Builds all permissions provided by .permissions.yml files.
*
* @return array[]
- * Each return permission is an array with the following keys:
- * - title: The title of the permission.
- * - description: The description of the permission, defaults to NULL.
- * - provider: The provider of the permission.
+ * An array with the same structure as
+ * PermissionHandlerInterface::getPermissions().
+ *
+ * @see \Drupal\user\PermissionHandlerInterface::getPermissions()
*/
protected function buildPermissionsYaml() {
$all_permissions = [];
@@ -193,10 +199,10 @@ protected function buildPermissionsYaml() {
* The permissions to be sorted.
*
* @return array[]
- * Each return permission is an array with the following keys:
- * - title: The title of the permission.
- * - description: The description of the permission, defaults to NULL.
- * - provider: The provider of the permission.
+ * An array with the same structure as
+ * PermissionHandlerInterface::getPermissions().
+ *
+ * @see \Drupal\user\PermissionHandlerInterface::getPermissions()
*/
protected function sortPermissions(array $all_permissions = []) {
// Get a list of all the modules providing permissions and sort by
diff --git a/core/modules/user/src/PermissionHandlerInterface.php b/core/modules/user/src/PermissionHandlerInterface.php
index 61526f3..ec70d8e 100644
--- a/core/modules/user/src/PermissionHandlerInterface.php
+++ b/core/modules/user/src/PermissionHandlerInterface.php
@@ -34,7 +34,20 @@
* permissions to have a clear, consistent security warning that is the
* same across the site. Use the 'description' key instead to provide any
* information that is specific to the permission you are defining.
+ * - dependencies: (optional) An array of dependency entities used when
+ * building this permission name, structured in the same way as the return
+ * of ConfigEntityInterface::calculateDependencies(). For example if this
+ * permission was generated as effect of the existence of node type
+ * 'article', then value of the dependency key is:
+ * @code
+ * 'dependencies' => ['config' => ['node.type.article']]
+ * @endcode
+ * The module providing this permission doesn't have to be added as
+ * dependency, because is automatically parsed, stored and retrieved from
+ * the 'provider' key.
* - provider: (optional) The provider name of the permission.
+ *
+ * @see \Drupal\Core\Config\Entity\ConfigEntityInterface::calculateDependencies()
*/
public function getPermissions();
diff --git a/core/modules/user/tests/src/Kernel/UserRoleDeleteTest.php b/core/modules/user/tests/src/Kernel/UserRoleDeleteTest.php
index cac470a..704b8d8 100644
--- a/core/modules/user/tests/src/Kernel/UserRoleDeleteTest.php
+++ b/core/modules/user/tests/src/Kernel/UserRoleDeleteTest.php
@@ -2,7 +2,11 @@
namespace Drupal\Tests\user\Kernel;
+use Drupal\Component\Utility\Unicode;
+use Drupal\filter\Entity\FilterFormat;
use Drupal\KernelTests\KernelTestBase;
+use Drupal\node\Entity\NodeType;
+use Drupal\user\Entity\Role;
use Drupal\user\Entity\User;
/**
@@ -71,4 +75,80 @@ public function testRoleDeleteUserRoleReferenceDelete() {
}
+ /**
+ * Tests the removal of user role dependencies.
+ */
+ public function testDependenciesRemoval() {
+ $this->enableModules(['node', 'filter']);
+ $this->installSchema('system', ['router']);
+ $this->container->get('router.builder')->rebuild();
+
+ /** @var \Drupal\user\RoleInterface $role */
+ $role = Role::create([
+ 'id' => $rid = Unicode::strtolower($this->randomMachineName()),
+ 'label' => $this->randomString(),
+ ]);
+ $role->save();
+
+ /** @var \Drupal\node\NodeTypeInterface $node_type */
+ $node_type = NodeType::create([
+ 'type' => Unicode::strtolower($this->randomMachineName()),
+ 'name' => $this->randomString(),
+ ]);
+ $node_type->save();
+ // Create a new text format to be used by role $role.
+ $format = FilterFormat::create([
+ 'format' => Unicode::strtolower($this->randomMachineName()),
+ 'name' => $this->randomString(),
+ ]);
+ $format->save();
+
+ $permission_format = "use text format {$format->id()}";
+ // Add two permissions with the same dependency to ensure both are removed
+ // and the role is not deleted.
+ $permission_node_type = "edit any {$node_type->id()} content";
+ $permission_node_type_create = "create {$node_type->id()} content";
+
+ // Grant $role permission to access content, use $format, edit $node_type.
+ $role
+ ->grantPermission('access content')
+ ->grantPermission($permission_format)
+ ->grantPermission($permission_node_type)
+ ->grantPermission($permission_node_type_create)
+ ->save();
+
+ // The role $role has the permissions to use $format and edit $node_type.
+ $role = Role::load($rid);
+ $this->assertTrue($role->hasPermission($permission_format));
+ $this->assertTrue($role->hasPermission($permission_node_type));
+ $this->assertTrue($role->hasPermission($permission_node_type_create));
+
+ // Remove the format.
+ $format->delete();
+
+ // The $role config entity exists after removing the config dependency.
+ $role = Role::load($rid);
+ $this->assertNotNull($role);
+ // The $format permission should have been revoked.
+ $this->assertFalse($role->hasPermission($permission_format));
+ $this->assertTrue($role->hasPermission($permission_node_type));
+ $this->assertTrue($role->hasPermission($permission_node_type_create));
+
+ // We have to manually trigger the removal of configuration belonging to the
+ // module because KernelTestBase::disableModules() is not aware of this.
+ $this->container->get('config.manager')->uninstall('module', 'node');
+ // Disable the node module.
+ $this->disableModules(['node']);
+
+ // The $role config entity exists after removing the module dependency.
+ $role = Role::load($rid);
+ $this->assertNotNull($role);
+ // The $node_type permission should have been revoked too.
+ $this->assertFalse($role->hasPermission($permission_format));
+ $this->assertFalse($role->hasPermission($permission_node_type));
+ $this->assertFalse($role->hasPermission($permission_node_type_create));
+ // The 'access content' permission should not have been revoked.
+ $this->assertTrue($role->hasPermission('access content'));
+ }
+
}