diff --git a/core/modules/block_content/block_content.permissions.yml b/core/modules/block_content/block_content.permissions.yml new file mode 100644 index 0000000000..18bcddd199 --- /dev/null +++ b/core/modules/block_content/block_content.permissions.yml @@ -0,0 +1,2 @@ +permission_callbacks: + - \Drupal\block_content\BlockContentPermissions::blockTypePermissions diff --git a/core/modules/block_content/src/BlockContentAccessControlHandler.php b/core/modules/block_content/src/BlockContentAccessControlHandler.php index 0c84190997..e19ceaefee 100644 --- a/core/modules/block_content/src/BlockContentAccessControlHandler.php +++ b/core/modules/block_content/src/BlockContentAccessControlHandler.php @@ -54,13 +54,22 @@ public static function createInstance(ContainerInterface $container, EntityTypeI * {@inheritdoc} */ protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account) { + // Allow view and update access to user with the 'edit any (type) block content' + // permission or the 'administer blocks' permission. + $edit_any_permission = 'edit any ' . $entity->bundle() . ' block content'; if ($operation === 'view') { $access = AccessResult::allowedIf($entity->isPublished()) - ->orIf(AccessResult::allowedIfHasPermission($account, 'administer blocks')); + ->orIf(AccessResult::allowedIfHasPermission($account, 'administer blocks')) + ->orIf(AccessResult::allowedIfHasPermission($account, $edit_any_permission)); + } + elseif ($operation === 'update') { + $access = AccessResult::allowedIfHasPermission($account, 'administer blocks') + ->orIf(AccessResult::allowedIfHasPermission($account, $edit_any_permission)); } else { $access = parent::checkAccess($entity, $operation, $account); } + // Add the entity as a cacheable dependency because access will at least be // determined by whether the block is reusable. $access->addCacheableDependency($entity); diff --git a/core/modules/block_content/src/BlockContentPermissions.php b/core/modules/block_content/src/BlockContentPermissions.php new file mode 100644 index 0000000000..ee63dda0d7 --- /dev/null +++ b/core/modules/block_content/src/BlockContentPermissions.php @@ -0,0 +1,50 @@ +buildPermission($type); + } + + return $perms; + } + + /** + * Return all the permissions available for a custom block type. + * + * @param \Drupal\block_content\Entity\BlockContentType $type + * The block type. + * + * @return array + * Permissions available for the given block type. + */ + protected function buildPermission(BlockContentType $type) { + $type_id = $type->id(); + $type_params = ['%type_name' => $type->label()]; + return [ + "edit any $type_id block content" => [ + 'title' => $this->t('%type_name: Edit any block content', $type_params), + ], + ]; + } + +} diff --git a/core/modules/block_content/tests/src/Kernel/BlockContentAccessHandlerTest.php b/core/modules/block_content/tests/src/Kernel/BlockContentAccessHandlerTest.php index 0f7e66c5c6..788d1d4c42 100644 --- a/core/modules/block_content/tests/src/Kernel/BlockContentAccessHandlerTest.php +++ b/core/modules/block_content/tests/src/Kernel/BlockContentAccessHandlerTest.php @@ -184,6 +184,22 @@ public function providerTestAccess() { NULL, 'allowed', ], + 'view:unpublished:reusable:per-block-editor:basic' => [ + 'view', + FALSE, + TRUE, + ['edit any basic block content'], + NULL, + 'neutral', + ], + 'view:unpublished:reusable:per-block-editor:square' => [ + 'view', + FALSE, + TRUE, + ['edit any square block content'], + NULL, + 'allowed', + ], 'view:published:reusable:admin' => [ 'view', TRUE, @@ -192,6 +208,22 @@ public function providerTestAccess() { NULL, 'allowed', ], + 'view:published:reusable:per-block-editor:basic' => [ + 'view', + TRUE, + TRUE, + ['edit any basic block content'], + NULL, + 'allowed', + ], + 'view:published:reusable:per-block-editor:square' => [ + 'view', + TRUE, + TRUE, + ['edit any square block content'], + NULL, + 'allowed', + ], 'view:published:non_reusable' => [ 'view', TRUE, @@ -291,8 +323,62 @@ public function providerTestAccess() { 'forbidden', 'forbidden', ], + $operation . ':unpublished:reusable:per-block-editor:basic' => [ + $operation, + FALSE, + TRUE, + ['edit any basic block content'], + NULL, + 'neutral', + ], + $operation . ':published:reusable:per-block-editor:basic' => [ + $operation, + TRUE, + TRUE, + ['edit any basic block content'], + NULL, + 'neutral', + ], ]; } + + $cases += [ + 'update:unpublished:reusable:per-block-editor:square' => [ + 'update', + FALSE, + TRUE, + ['edit any square block content'], + NULL, + 'allowed', + ], + 'update:published:reusable:per-block-editor:square' => [ + 'update', + TRUE, + TRUE, + ['edit any square block content'], + NULL, + 'allowed', + ], + ]; + + $cases += [ + 'delete:unpublished:reusable:per-block-editor:square' => [ + 'delete', + FALSE, + TRUE, + ['edit any square block content'], + NULL, + 'neutral', + ], + 'delete:published:reusable:per-block-editor:square' => [ + 'delete', + TRUE, + TRUE, + ['edit any square block content'], + NULL, + 'neutral', + ], + ]; return $cases; } diff --git a/core/modules/block_content/tests/src/Kernel/BlockContentPermissionsTest.php b/core/modules/block_content/tests/src/Kernel/BlockContentPermissionsTest.php new file mode 100644 index 0000000000..eba2cf4d1c --- /dev/null +++ b/core/modules/block_content/tests/src/Kernel/BlockContentPermissionsTest.php @@ -0,0 +1,86 @@ +installSchema('system', ['sequences']); + $this->installEntitySchema('user'); + $this->installEntitySchema('block_content'); + + $this->permissionHandler = $this->container->get('user.permissions'); + } + + /** + * @covers ::blockTypePermissions + */ + public function testDynamicPermissions() { + $permissions = $this->permissionHandler->getPermissions(); + $this->assertArrayNotHasKey('edit any basic block content', $permissions, 'The per-block-type permission does not exist.'); + $this->assertArrayNotHasKey('edit any square block content', $permissions, 'The per-block-type permission does not exist.'); + + // Create a basic block content type. + BlockContentType::create([ + 'id' => 'basic', + 'label' => 'A basic block type', + 'description' => 'Provides a basic block type', + ])->save(); + + // Create a square block content type. + BlockContentType::create([ + 'id' => 'square', + 'label' => 'A square block type', + 'description' => 'Provides a block type that is square', + ])->save(); + + $permissions = $this->permissionHandler->getPermissions(); + + // Assert the basic permission has been created. + $this->assertArrayHasKey('edit any basic block content', $permissions, 'The per-block-type permission exists.'); + $this->assertEquals( + 'A basic block type: Edit any block content', + $permissions['edit any basic block content']['title']->render() + ); + + // Assert the square permission has been created. + $this->assertArrayHasKey('edit any square block content', $permissions, 'The per-block-type permission exists.'); + $this->assertEquals( + 'A square block type: Edit any block content', + $permissions['edit any square block content']['title']->render() + ); + } + +}