From f48af6d533240d5a0d480747c9ca13c8069421a5 Mon Sep 17 00:00:00 2001 From: Brian Canini <canini.16@osu.edu> Date: Thu, 22 Apr 2021 13:19:55 -0400 Subject: [PATCH] Upgrading drupal/block_permissions (1.1.0 => 1.2.0) --- composer.json | 2 +- composer.lock | 16 +- vendor/composer/InstalledVersions.php | 10 +- vendor/composer/installed.json | 20 +- vendor/composer/installed.php | 10 +- web/modules/block_permissions/README.txt | 3 + .../block_permissions.info.yml | 8 +- .../block_permissions.module | 13 +- .../BlockPermissionsAccessControlHandler.php | 100 +++----- .../src/BlockPermissionsPermissions.php | 4 +- .../src/Routing/RouteSubscriber.php | 23 +- .../Functional/BlockFormRoutesAccessTest.php | 227 ++++++++++++++++++ .../src/Functional/BlockListElementsTest.php | 110 +++++++++ .../Functional/BlockListsRoutesAccessTest.php | 113 +++++++++ ...kPermissionsBlockLibraryControllerTest.php | 100 ++++++++ .../BlockPermissionsBrowserTestBase.php | 60 +++++ 16 files changed, 709 insertions(+), 110 deletions(-) create mode 100644 web/modules/block_permissions/tests/src/Functional/BlockFormRoutesAccessTest.php create mode 100644 web/modules/block_permissions/tests/src/Functional/BlockListElementsTest.php create mode 100644 web/modules/block_permissions/tests/src/Functional/BlockListsRoutesAccessTest.php create mode 100644 web/modules/block_permissions/tests/src/Functional/BlockPermissionsBlockLibraryControllerTest.php create mode 100644 web/modules/block_permissions/tests/src/Functional/BlockPermissionsBrowserTestBase.php diff --git a/composer.json b/composer.json index 3935560216..681e088fe2 100644 --- a/composer.json +++ b/composer.json @@ -95,7 +95,7 @@ "drupal/better_exposed_filters": "5.0-beta1", "drupal/bigmenu": "1.0.0-alpha1", "drupal/block_field": "1.0.0-rc1", - "drupal/block_permissions": "1.1", + "drupal/block_permissions": "1.2", "drupal/block_region_permissions": "1.4", "drupal/bootstrap": "^3.23", "drupal/cache_control_override": "^1.0@alpha", diff --git a/composer.lock b/composer.lock index 95a1af1d50..99f3364211 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": "93b578a3ad1512529d48e473d4725c6d", + "content-hash": "3db5c29743b11cc570dd76324a99caf4", "packages": [ { "name": "alchemy/zippy", @@ -2620,17 +2620,17 @@ }, { "name": "drupal/block_permissions", - "version": "1.1.0", + "version": "1.2.0", "source": { "type": "git", "url": "https://git.drupalcode.org/project/block_permissions.git", - "reference": "8.x-1.1" + "reference": "8.x-1.2" }, "dist": { "type": "zip", - "url": "https://ftp.drupal.org/files/projects/block_permissions-8.x-1.1.zip", - "reference": "8.x-1.1", - "shasum": "734c00f78dfb674294b2d35bc87fcaed7ecf2042" + "url": "https://ftp.drupal.org/files/projects/block_permissions-8.x-1.2.zip", + "reference": "8.x-1.2", + "shasum": "45c63cf14da18d82c73049c8dcdf90fde6a107b5" }, "require": { "drupal/core": "^8 || ^9" @@ -2638,8 +2638,8 @@ "type": "drupal-module", "extra": { "drupal": { - "version": "8.x-1.1", - "datestamp": "1592480089", + "version": "8.x-1.2", + "datestamp": "1617805905", "security-coverage": { "status": "covered", "message": "Covered by Drupal's security advisory policy" diff --git a/vendor/composer/InstalledVersions.php b/vendor/composer/InstalledVersions.php index 2a5b6098c2..fe1765b93c 100644 --- a/vendor/composer/InstalledVersions.php +++ b/vendor/composer/InstalledVersions.php @@ -30,7 +30,7 @@ class InstalledVersions 'aliases' => array ( ), - 'reference' => 'd8b57c0738b50e066eb7a9043a57235f4340efa0', + 'reference' => '0885fcdf780a5c0917f015db12ad5ee6b4f78ccc', 'name' => 'osu-asc-webservices/d8-upstream', ), 'versions' => @@ -492,12 +492,12 @@ class InstalledVersions ), 'drupal/block_permissions' => array ( - 'pretty_version' => '1.1.0', - 'version' => '1.1.0.0', + 'pretty_version' => '1.2.0', + 'version' => '1.2.0.0', 'aliases' => array ( ), - 'reference' => '8.x-1.1', + 'reference' => '8.x-1.2', ), 'drupal/block_place' => array ( @@ -2431,7 +2431,7 @@ class InstalledVersions 'aliases' => array ( ), - 'reference' => 'd8b57c0738b50e066eb7a9043a57235f4340efa0', + 'reference' => '0885fcdf780a5c0917f015db12ad5ee6b4f78ccc', ), 'pantheon-systems/quicksilver-pushback' => array ( diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index 49328e8447..d451e18de0 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -2629,18 +2629,18 @@ }, { "name": "drupal/block_permissions", - "version": "1.1.0", - "version_normalized": "1.1.0.0", + "version": "1.2.0", + "version_normalized": "1.2.0.0", "source": { "type": "git", "url": "https://git.drupalcode.org/project/block_permissions.git", - "reference": "8.x-1.1" + "reference": "8.x-1.2" }, "dist": { "type": "zip", - "url": "https://ftp.drupal.org/files/projects/block_permissions-8.x-1.1.zip", - "reference": "8.x-1.1", - "shasum": "734c00f78dfb674294b2d35bc87fcaed7ecf2042" + "url": "https://ftp.drupal.org/files/projects/block_permissions-8.x-1.2.zip", + "reference": "8.x-1.2", + "shasum": "45c63cf14da18d82c73049c8dcdf90fde6a107b5" }, "require": { "drupal/core": "^8 || ^9" @@ -2648,8 +2648,8 @@ "type": "drupal-module", "extra": { "drupal": { - "version": "8.x-1.1", - "datestamp": "1592480089", + "version": "8.x-1.2", + "datestamp": "1617805905", "security-coverage": { "status": "covered", "message": "Covered by Drupal's security advisory policy" @@ -2673,6 +2673,10 @@ { "name": "michielnugter", "homepage": "https://www.drupal.org/user/1023784" + }, + { + "name": "sinn", + "homepage": "https://www.drupal.org/user/682600" } ], "description": "Adds specific permissions for administering blocks.", diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php index 094ccc5ee2..11ec43120c 100644 --- a/vendor/composer/installed.php +++ b/vendor/composer/installed.php @@ -6,7 +6,7 @@ 'aliases' => array ( ), - 'reference' => 'd8b57c0738b50e066eb7a9043a57235f4340efa0', + 'reference' => '0885fcdf780a5c0917f015db12ad5ee6b4f78ccc', 'name' => 'osu-asc-webservices/d8-upstream', ), 'versions' => @@ -468,12 +468,12 @@ ), 'drupal/block_permissions' => array ( - 'pretty_version' => '1.1.0', - 'version' => '1.1.0.0', + 'pretty_version' => '1.2.0', + 'version' => '1.2.0.0', 'aliases' => array ( ), - 'reference' => '8.x-1.1', + 'reference' => '8.x-1.2', ), 'drupal/block_place' => array ( @@ -2407,7 +2407,7 @@ 'aliases' => array ( ), - 'reference' => 'd8b57c0738b50e066eb7a9043a57235f4340efa0', + 'reference' => '0885fcdf780a5c0917f015db12ad5ee6b4f78ccc', ), 'pantheon-systems/quicksilver-pushback' => array ( diff --git a/web/modules/block_permissions/README.txt b/web/modules/block_permissions/README.txt index 085a1b670e..e586f4be6a 100644 --- a/web/modules/block_permissions/README.txt +++ b/web/modules/block_permissions/README.txt @@ -44,6 +44,9 @@ blocks. Assign the permissions per theme and provider to each role you wish. +For Block layout page (/admin/structure/block) permission 'administer blocks' is not enough. User should be assigned +permission to administer block settings for the default theme also. + MAINTAINERS ----------- diff --git a/web/modules/block_permissions/block_permissions.info.yml b/web/modules/block_permissions/block_permissions.info.yml index 30fd7150de..0d0bfc479f 100644 --- a/web/modules/block_permissions/block_permissions.info.yml +++ b/web/modules/block_permissions/block_permissions.info.yml @@ -1,11 +1,11 @@ -name: 'Block permissions' +name: 'Block Permissions' type: module description: 'Adds specific permissions for administering blocks.' package: Block core: '8.x' core_version_requirement: ^8 || ^9 -# Information added by Drupal.org packaging script on 2020-06-18 -version: '8.x-1.1' +# Information added by Drupal.org packaging script on 2021-04-07 +version: '8.x-1.2' project: 'block_permissions' -datestamp: 1592480090 +datestamp: 1617805907 diff --git a/web/modules/block_permissions/block_permissions.module b/web/modules/block_permissions/block_permissions.module index 2bc1af1fd4..85508809a2 100644 --- a/web/modules/block_permissions/block_permissions.module +++ b/web/modules/block_permissions/block_permissions.module @@ -5,9 +5,9 @@ * Block permissions module. */ -use \Drupal\Core\Render\Element; -use \Drupal\block\Entity\Block; -use \Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Render\Element; +use Drupal\block\Entity\Block; +use Drupal\Core\Form\FormStateInterface; /** * Implements hook_form_FORM_ID_alter(). @@ -32,10 +32,9 @@ function block_permissions_form_block_admin_display_form_alter(&$form, FormState $form['blocks'][$key]['operations']['#access'] = FALSE; // Remove the draggable class and replace it with undraggable. - foreach ($form['blocks'][$key]['#attributes']['class'] as $class_key => $class) { - if ($class = 'draggable') { - $form['blocks'][$key]['#attributes']['class'][$class_key] = 'undraggable'; - } + $class_key = array_search('draggable', $form['blocks'][$key]['#attributes']['class']); + if ($class_key !== FALSE) { + $form['blocks'][$key]['#attributes']['class'][$class_key] = 'undraggable'; } } } diff --git a/web/modules/block_permissions/src/BlockPermissionsAccessControlHandler.php b/web/modules/block_permissions/src/BlockPermissionsAccessControlHandler.php index 461d4eb0e2..d744c5ecd8 100644 --- a/web/modules/block_permissions/src/BlockPermissionsAccessControlHandler.php +++ b/web/modules/block_permissions/src/BlockPermissionsAccessControlHandler.php @@ -5,8 +5,10 @@ use Drupal\block\Entity\Block; use Drupal\Core\Access\AccessResult; use Drupal\Core\DependencyInjection\ContainerInjectionInterface; -use Symfony\Component\DependencyInjection\ContainerInterface; +use Drupal\Core\Session\AccountInterface; use Drupal\Core\Block\BlockManagerInterface; +use Drupal\Core\Config\ConfigFactoryInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * Controller for the block permissions. @@ -18,7 +20,7 @@ class BlockPermissionsAccessControlHandler implements ContainerInjectionInterfac * * @var \Drupal\Core\Block\BlockManagerInterface */ - protected $manager; + protected $blockManager; /** * The current user service. @@ -27,12 +29,21 @@ class BlockPermissionsAccessControlHandler implements ContainerInjectionInterfac */ protected $currentUser; + /** + * The config factory service. + * + * @var \Drupal\Core\Config\ConfigFactoryInterface + */ + protected $configFactory; + /** * {@inheritdoc} */ public static function create(ContainerInterface $container) { return new static( - $container->get('plugin.manager.block') + $container->get('plugin.manager.block'), + $container->get('current_user'), + $container->get('config.factory'), ); } @@ -40,39 +51,17 @@ public static function create(ContainerInterface $container) { /** * Constructs the block access control handler instance. * - * @param \Drupal\Core\Block\BlockManagerInterface $manager + * @param \Drupal\Core\Block\BlockManagerInterface $block_manager * Plugin manager. + * @param \Drupal\Core\Session\AccountInterface $current_user + * Current user. + * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory + * The config factory service. */ - public function __construct(BlockManagerInterface $manager) { - $this->manager = $manager; - } - - /** - * Returns the service container. - * - * This method is marked private to prevent sub-classes from retrieving - * services from the container through it. Instead, - * \Drupal\Core\DependencyInjection\ContainerInjectionInterface should be used - * for injecting services. - * - * @return \Symfony\Component\DependencyInjection\ContainerInterface - * The service container. - */ - private function container() { - return \Drupal::getContainer(); - } - - /** - * Returns the current user. - * - * @return \Drupal\Core\Session\AccountInterface - * The current user. - */ - protected function currentUser() { - if (!$this->currentUser) { - $this->currentUser = $this->container()->get('current_user'); - } - return $this->currentUser; + public function __construct(BlockManagerInterface $block_manager, AccountInterface $current_user, ConfigFactoryInterface $config_factory) { + $this->blockManager = $block_manager; + $this->currentUser = $current_user; + $this->configFactory = $config_factory; } /** @@ -82,22 +71,11 @@ protected function currentUser() { * AccessResult object. */ public function blockListAccess() { - $account = $this->currentUser(); - - $theme = \Drupal::config('system.theme')->get('default'); + $theme = $this->configFactory->get('system.theme')->get('default'); // Check if the user has the proper permissions. - $access = AccessResult::allowedIfHasPermission($account, 'administer block settings for theme ' . $theme); - - // If the user doesn't have access to the default theme validate if the user - // has the generic permission administer blocks, if so: log an error stating - // the default access is required in this case. - if (!$access->isAllowed()) { - $genericAccess = AccessResult::allowedIfHasPermission($account, 'administer blocks'); - if ($genericAccess->isAllowed()) { - \Drupal::logger('block_permissions')->error('User @user has the administer block settings permission but no access to the default theme @theme, this is required.', array('@user' => $account->getAccountName(), '@theme' => $theme)); - } - } + $access = AccessResult::allowedIfHasPermission($this->currentUser, 'administer block settings for theme ' . $theme); + return $access; } @@ -108,13 +86,11 @@ public function blockListAccess() { * The theme name. * * @return \Drupal\Core\Access\AccessResultInterface - * An access result + * An access result. */ public function blockThemeListAccess($theme) { - $account = $this->currentUser(); - // Check if the user has the proper permissions. - $access = AccessResult::allowedIfHasPermission($account, 'administer block settings for theme ' . $theme); + $access = AccessResult::allowedIfHasPermission($this->currentUser, 'administer block settings for theme ' . $theme); return $access; } @@ -123,17 +99,21 @@ public function blockThemeListAccess($theme) { * Access check for the add block form. * * @param string $plugin_id + * The plugin name. + * @param string $theme * The theme name. * * @return \Drupal\Core\Access\AccessResult - * An access result + * An access result. */ - public function blockAddFormAccess($plugin_id) { - $account = $this->currentUser(); - $plugin = $this->manager->getDefinition($plugin_id); + public function blockAddFormAccess($plugin_id, $theme) { + $plugin = $this->blockManager->getDefinition($plugin_id); // Check if the user has the proper permissions. - $access = AccessResult::allowedIfHasPermission($account, 'administer blocks provided by ' . $plugin['provider']); + $access = AccessResult::allowedIfHasPermissions($this->currentUser, [ + 'administer blocks provided by ' . $plugin['provider'], + 'administer block settings for theme ' . $theme, + ]); return $access; } @@ -145,16 +125,14 @@ public function blockAddFormAccess($plugin_id) { * The theme name. * * @return \Drupal\Core\Access\AccessResultInterface - * An access result + * An access result. */ public function blockFormAccess(Block $block) { - $account = $this->currentUser(); - $plugin = $block->getPlugin(); $configuration = $plugin->getConfiguration(); // Check if the user has the proper permissions. - $access = AccessResult::allowedIfHasPermission($account, 'administer blocks provided by ' . $configuration['provider']); + $access = AccessResult::allowedIfHasPermission($this->currentUser, 'administer blocks provided by ' . $configuration['provider']); return $access; } diff --git a/web/modules/block_permissions/src/BlockPermissionsPermissions.php b/web/modules/block_permissions/src/BlockPermissionsPermissions.php index 9a158982db..b495c06120 100644 --- a/web/modules/block_permissions/src/BlockPermissionsPermissions.php +++ b/web/modules/block_permissions/src/BlockPermissionsPermissions.php @@ -32,6 +32,8 @@ class BlockPermissionsPermissions implements ContainerInjectionInterface { /** * Constructs a new BlockPermissionsPermissions instance. * + * @param \Drupal\Core\Block\BlockManagerInterface $block_manager + * The block manager. * @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler * The theme handler. */ @@ -73,7 +75,7 @@ public function permissions() { // Create a permission for each block category. $definitions = $this->blockManager->getDefinitions(); - $providers = array(); + $providers = []; foreach ($definitions as $definition) { $providers[$definition['provider']] = $definition['provider']; } diff --git a/web/modules/block_permissions/src/Routing/RouteSubscriber.php b/web/modules/block_permissions/src/Routing/RouteSubscriber.php index 5219991f30..cbb2f7ef04 100644 --- a/web/modules/block_permissions/src/Routing/RouteSubscriber.php +++ b/web/modules/block_permissions/src/Routing/RouteSubscriber.php @@ -17,42 +17,45 @@ public function alterRoutes(RouteCollection $collection) { // Change access callback for the blocks list per theme to our // permission-per-theme based version. if ($route = $collection->get('block.admin_display_theme')) { - $route->addRequirements(array( + $route->addRequirements([ '_custom_access' => "\\Drupal\\block_permissions\\BlockPermissionsAccessControlHandler::blockThemeListAccess", - )); + ]); } // Change access callback for the default block management page. if ($route = $collection->get('block.admin_display')) { - $route->addRequirements(array( + $route->addRequirements([ '_custom_access' => "\\Drupal\\block_permissions\\BlockPermissionsAccessControlHandler::blockListAccess", - )); + ]); } // Change the access callback for the add form of a block. if ($route = $collection->get('block.admin_add')) { - $route->addRequirements(array( + $route->addRequirements([ '_custom_access' => "\\Drupal\\block_permissions\\BlockPermissionsAccessControlHandler::blockAddFormAccess", - )); + ]); } // Change the access callback for the edit form of a block. if ($route = $collection->get('entity.block.edit_form')) { - $route->addRequirements(array( + $route->addRequirements([ '_custom_access' => "\\Drupal\\block_permissions\\BlockPermissionsAccessControlHandler::blockFormAccess", - )); + ]); } // Change the access callback for the delete form of a block. if ($route = $collection->get('entity.block.delete_form')) { - $route->addRequirements(array( + $route->addRequirements([ '_custom_access' => "\\Drupal\\block_permissions\\BlockPermissionsAccessControlHandler::blockFormAccess", - )); + ]); } // Change the controller for the block admin_library. if ($route = $collection->get('block.admin_library')) { $route->setDefault('_controller', '\\Drupal\\block_permissions\\Controller\\BlockPermissionsBlockLibraryController::listBlocks'); + $route->addRequirements([ + '_custom_access' => "\\Drupal\\block_permissions\\BlockPermissionsAccessControlHandler::blockThemeListAccess", + ]); } } diff --git a/web/modules/block_permissions/tests/src/Functional/BlockFormRoutesAccessTest.php b/web/modules/block_permissions/tests/src/Functional/BlockFormRoutesAccessTest.php new file mode 100644 index 0000000000..8822732522 --- /dev/null +++ b/web/modules/block_permissions/tests/src/Functional/BlockFormRoutesAccessTest.php @@ -0,0 +1,227 @@ +<?php + +namespace Drupal\Tests\block_permissions\Functional; + +use Drupal\Component\Render\FormattableMarkup; +use Drupal\Core\Url; + +/** + * Tests Block permissions access control handler for block configuration pages. + * + * @coversDefaultClass \Drupal\block_permissions\BlockPermissionsAccessControlHandler + * + * @group block_permissions + */ +class BlockFormRoutesAccessTest extends BlockPermissionsBrowserTestBase { + + /** + * User with permissions to administer core blocks. + * + * @var \Drupal\user\UserInterface + */ + protected $coreBlocksUser; + + /** + * User with permissions to administer system module blocks. + * + * @var \Drupal\user\UserInterface + */ + protected $systemBlocksUser; + + /** + * User with permissions to administer blocks in second theme. + * + * @var \Drupal\user\UserInterface + */ + protected $secondThemeUser; + + /** + * {@inheritdoc} + */ + protected function setUp(): void { + parent::setUp(); + + // User can administer blocks from core. + $this->coreBlocksUser = $this->drupalCreateUser([ + 'administer blocks', + "administer block settings for theme $this->defaultTheme", + 'administer blocks provided by core', + ]); + + // User can administer blocks from system module. + $this->systemBlocksUser = $this->drupalCreateUser([ + 'administer blocks', + "administer block settings for theme $this->defaultTheme", + 'administer blocks provided by system', + ]); + + // User can administer blocks from core but in second theme only. + $this->secondThemeUser = $this->drupalCreateUser([ + 'administer blocks', + "administer block settings for theme $this->secondTheme", + 'administer blocks provided by core', + 'administer blocks provided by system', + ]); + } + + /** + * Tests access to "/admin/structure/block/add/{plugin_id}/{theme}" page. + * + * @covers ::blockAddFormAccess + */ + public function testBlockAddFormAccess() { + // Ensure that the user with permissions to administer blocks in the default + // theme can create core's blocks only. + $this->drupalLogin($this->coreBlocksUser); + $this->drupalGet($this->getBlockAdminAddUrl('page_title_block', $this->defaultTheme)); + $this->assertBlockFormPageHasAccess(); + $this->drupalGet($this->getBlockAdminAddUrl('system_branding_block', $this->defaultTheme)); + $this->assertSession()->statusCodeEquals(403); + + // Ensure that the user with permissions to administer blocks in the default + // theme can create system's blocks only. + $this->drupalLogin($this->systemBlocksUser); + $this->drupalGet($this->getBlockAdminAddUrl('page_title_block', $this->defaultTheme)); + $this->assertSession()->statusCodeEquals(403); + $this->drupalGet($this->getBlockAdminAddUrl('system_branding_block', $this->defaultTheme)); + $this->assertBlockFormPageHasAccess(); + + // Ensure that the user can add blocks only to a theme where it can + // administer blocks. + $this->drupalLogin($this->secondThemeUser); + $this->drupalGet($this->getBlockAdminAddUrl('page_title_block', $this->secondTheme)); + $this->assertBlockFormPageHasAccess(); + $this->drupalGet($this->getBlockAdminAddUrl('system_branding_block', $this->secondTheme)); + $this->assertBlockFormPageHasAccess(); + $this->drupalGet($this->getBlockAdminAddUrl('page_title_block', $this->defaultTheme)); + $this->assertSession()->statusCodeEquals(403); + $this->drupalGet($this->getBlockAdminAddUrl('system_branding_block', $this->defaultTheme)); + $this->assertSession()->statusCodeEquals(403); + } + + /** + * Tests the access to the block edit page. + * + * Path to test: + * "/admin/structure/block/manage/{block}", + * + * $covers ::blockFormAccess + */ + public function testBlockEditFormAccess() { + $page_title_block_edit = $this->getBlockEditFormUrl($this->pageTitleBlock->id()); + $system_branding_block_edit = $this->getBlockEditFormUrl($this->systemBrandingBlock->id()); + + // Ensure user can edit block from core only. + $this->drupalLogin($this->coreBlocksUser); + $this->drupalGet($page_title_block_edit); + $this->assertBlockFormPageHasAccess(); + $this->drupalGet($system_branding_block_edit); + $this->assertSession()->statusCodeEquals(403); + + // Ensure user can edit block from system module only. + $this->drupalLogin($this->systemBlocksUser); + $this->drupalGet($page_title_block_edit); + $this->assertSession()->statusCodeEquals(403); + $this->drupalGet($system_branding_block_edit); + $this->assertBlockFormPageHasAccess(); + } + + /** + * Tests the access to the block delete page. + * + * Path to test: + * "/admin/structure/block/manage/{block}/delete". + * + * $covers ::blockFormAccess + */ + public function testBlockDeleteFormAccess() { + $page_title_block_delete = $this->getBlockDeleteFormUrl($this->pageTitleBlock->id()); + $system_branding_block_delete = $this->getBlockDeleteFormUrl($this->systemBrandingBlock->id()); + + // Ensure user can delete block from core only. + $this->drupalLogin($this->coreBlocksUser); + $this->drupalGet($page_title_block_delete); + $this->assertSession()->statusCodeEquals(200); + $this->assertSession()->pageTextContains($this->getBlockDeleteFormTitle($this->pageTitleBlock->label())); + $this->drupalGet($system_branding_block_delete); + $this->assertSession()->statusCodeEquals(403); + + // Ensure user can delete block from system module only. + $this->drupalLogin($this->systemBlocksUser); + $this->drupalGet($page_title_block_delete); + $this->assertSession()->statusCodeEquals(403); + $this->drupalGet($system_branding_block_delete); + $this->assertSession()->statusCodeEquals(200); + $this->assertSession()->pageTextContains($this->getBlockDeleteFormTitle($this->systemBrandingBlock->label())); + } + + /** + * Asserts that user has the access to the page. + */ + protected function assertBlockFormPageHasAccess() { + $this->assertSession()->statusCodeEquals(200); + $this->assertSession()->pageTextContains('Configure block'); + } + + /** + * Gets the path to the block edit form. + * + * @param string $id + * Block id. + * + * @return \Drupal\Core\Url + * A new Url object for a routed URL. + */ + protected function getBlockEditFormUrl($id) { + return Url::fromRoute('entity.block.edit_form', [ + 'block' => $id, + ]); + } + + /** + * Gets the path to the block delete form. + * + * @param string $id + * Block id. + * + * @return \Drupal\Core\Url + * A new Url object for a routed URL. + */ + protected function getBlockDeleteFormUrl($id) { + return Url::fromRoute('entity.block.delete_form', [ + 'block' => $id, + ]); + } + + /** + * Gets the path to block add form. + * + * @param string $id + * Block id. + * @param string $theme + * Theme name. + * + * @return \Drupal\Core\Url + * A new Url object for a routed URL. + */ + protected function getBlockAdminAddUrl($id, $theme) { + return Url::fromRoute('block.admin_add', [ + 'plugin_id' => $id, + 'theme' => $theme, + ]); + } + + /** + * Gets the title of the block deletion page. + * + * @param string $name + * Block name. + * + * @return \Drupal\Component\Render\MarkupInterface + * Page title. + */ + protected function getBlockDeleteFormTitle($name) { + return new FormattableMarkup('Are you sure you want to remove the block @name?', ['@name' => $name]); + } + +} diff --git a/web/modules/block_permissions/tests/src/Functional/BlockListElementsTest.php b/web/modules/block_permissions/tests/src/Functional/BlockListElementsTest.php new file mode 100644 index 0000000000..0837ba198d --- /dev/null +++ b/web/modules/block_permissions/tests/src/Functional/BlockListElementsTest.php @@ -0,0 +1,110 @@ +<?php + +namespace Drupal\Tests\block_permissions\Functional; + +use Drupal\Core\Url; + +/** + * Tests visibility of configuration elements on the block_admin_display_form. + * + * @group block_permissions + */ +class BlockListElementsTest extends BlockPermissionsBrowserTestBase { + + /** + * User with permissions to administer core blocks. + * + * @var \Drupal\user\UserInterface + */ + protected $coreBlocksUser; + + /** + * User with permissions to administer system module blocks. + * + * @var \Drupal\user\UserInterface + */ + protected $systemBlocksUser; + + /** + * {@inheritdoc} + */ + protected function setUp(): void { + parent::setUp(); + + // User can administer blocks from core. + $this->coreBlocksUser = $this->drupalCreateUser([ + 'administer blocks', + "administer block settings for theme $this->defaultTheme", + 'administer blocks provided by core', + ]); + + // User can administer blocks from system module. + $this->systemBlocksUser = $this->drupalCreateUser([ + 'administer blocks', + "administer block settings for theme $this->defaultTheme", + 'administer blocks provided by system', + ]); + } + + /** + * Tests configuration elements on the block list page. + * + * Page for testing - "/admin/structure/block". + */ + public function testBlockListPage() { + $block_admin_display_path = Url::fromRoute('block.admin_display'); + + // Ensure user sees blocks but can administer block from core only. + $this->drupalLogin($this->coreBlocksUser); + $this->drupalGet($block_admin_display_path); + $this->assertSession()->pageTextContains($this->pageTitleBlock->label()); + $this->assertSession()->pageTextContains($this->systemBrandingBlock->label()); + $this->assertBlockElementsExists($this->pageTitleBlock->id()); + $this->assertBlockElementsNotExists($this->systemBrandingBlock->id()); + + // Ensure user sees blocks but can administer block from system module only. + $this->drupalLogin($this->systemBlocksUser); + $this->drupalGet($block_admin_display_path); + $this->assertSession()->pageTextContains($this->pageTitleBlock->label()); + $this->assertSession()->pageTextContains($this->systemBrandingBlock->label()); + $this->assertBlockElementsNotExists($this->pageTitleBlock->id()); + $this->assertBlockElementsExists($this->systemBrandingBlock->id()); + } + + /** + * Asserts that block configuration elements exist. + * + * @param string $id + * Block id. + */ + protected function assertBlockElementsExists($id) { + $row = $this->assertSession()->elementExists('css', "tr[data-drupal-selector=\"edit-blocks-$id\"]"); + // Configurations dropbutton exists. + $this->assertSession()->elementExists('css', 'ul.dropbutton', $row); + // Weight selector exists. + $this->assertSession()->elementExists('css', 'select.block-weight', $row); + // Region selector exists. + $this->assertSession()->elementExists('css', 'select.block-region-select', $row); + // Draggable element exists. + $this->assertStringContainsString('draggable', $row->getAttribute('class')); + } + + /** + * Asserts that block configuration elements don't exist. + * + * @param string $id + * Block id. + */ + protected function assertBlockElementsNotExists($id) { + $row = $this->assertSession()->elementExists('css', "tr[data-drupal-selector=\"edit-blocks-$id\"]"); + // Configurations dropbutton doesn't exists. + $this->assertSession()->elementNotExists('css', 'ul.dropbutton', $row); + // Weight selector doesn't exists. + $this->assertSession()->elementNotExists('css', 'select.block-weight', $row); + // Region selector doesn't exist. + $this->assertSession()->elementNotExists('css', 'select.block-region-select', $row); + // Draggable element doesn't exists. + $this->assertStringContainsString('undraggable', $row->getAttribute('class')); + } + +} diff --git a/web/modules/block_permissions/tests/src/Functional/BlockListsRoutesAccessTest.php b/web/modules/block_permissions/tests/src/Functional/BlockListsRoutesAccessTest.php new file mode 100644 index 0000000000..dd901fd690 --- /dev/null +++ b/web/modules/block_permissions/tests/src/Functional/BlockListsRoutesAccessTest.php @@ -0,0 +1,113 @@ +<?php + +namespace Drupal\Tests\block_permissions\Functional; + +use Drupal\Core\Url; + +/** + * Tests Block permissions access control handler for block list pages. + * + * @coversDefaultClass \Drupal\block_permissions\BlockPermissionsAccessControlHandler + * + * @group block_permissions + */ +class BlockListsRoutesAccessTest extends BlockPermissionsBrowserTestBase { + + /** + * User with permissions to administer blocks in the default theme. + * + * @var \Drupal\user\UserInterface + */ + protected $defaultThemeUser; + + /** + * User with permissions to administer blocks in the second theme. + * + * @var \Drupal\user\UserInterface + */ + protected $secondThemeUser; + + /** + * {@inheritdoc} + */ + protected function setUp(): void { + parent::setUp(); + + $this->defaultThemeUser = $this->drupalCreateUser([ + 'administer blocks', + "administer block settings for theme $this->defaultTheme", + ]); + $this->secondThemeUser = $this->drupalCreateUser([ + 'administer blocks', + "administer block settings for theme $this->secondTheme", + ]); + } + + /** + * Test the access to the "/admin/structure/block" page. + * + * @covers ::blockListAccess + */ + public function testBlockListAccess() { + $block_admin_display_path = Url::fromRoute('block.admin_display'); + + // Ensure the user has the access to the list of blocks of the default + // theme. + $this->drupalLogin($this->defaultThemeUser); + $this->drupalGet($block_admin_display_path); + $this->assertBlockListPageHasAccess(); + + // Ensure the user doesn't have the access to the list of blocks of the + // default theme. + $this->drupalLogin($this->secondThemeUser); + $this->drupalGet($block_admin_display_path); + $this->assertSession()->statusCodeEquals(403); + } + + /** + * Test the access to the "/admin/structure/block/list/{theme}" page. + * + * @covers ::blockThemeListAccess + */ + public function testBlockThemeListAccess() { + // Ensure the user has the access to the list of blocks of the default theme + // only. + $this->drupalLogin($this->defaultThemeUser); + $this->drupalGet($this->getBlockAdminDisplayThemeUrl($this->defaultTheme)); + $this->assertBlockListPageHasAccess(); + $this->drupalGet($this->getBlockAdminDisplayThemeUrl($this->secondTheme)); + $this->assertSession()->statusCodeEquals(403); + + // Ensure the user has the access to the list of blocks of the second theme + // only. + $this->drupalLogin($this->secondThemeUser); + $this->drupalGet($this->getBlockAdminDisplayThemeUrl($this->defaultTheme)); + $this->assertSession()->statusCodeEquals(403); + $this->drupalGet($this->getBlockAdminDisplayThemeUrl($this->secondTheme)); + $this->assertBlockListPageHasAccess(); + } + + /** + * Gets the URL of the block list page. + * + * @param string $theme + * Theme name. + * + * @return \Drupal\Core\Url + * A new Url object for a routed URL. + */ + protected function getBlockAdminDisplayThemeUrl($theme) { + return Url::fromRoute('block.admin_display_theme', [ + 'theme' => $theme, + ]); + } + + /** + * Asserts that the user has the access to the block list page. + */ + protected function assertBlockListPageHasAccess() { + $this->assertSession()->statusCodeEquals(200); + $this->assertSession()->pageTextContains('Block layout'); + } + +} diff --git a/web/modules/block_permissions/tests/src/Functional/BlockPermissionsBlockLibraryControllerTest.php b/web/modules/block_permissions/tests/src/Functional/BlockPermissionsBlockLibraryControllerTest.php new file mode 100644 index 0000000000..d80043a332 --- /dev/null +++ b/web/modules/block_permissions/tests/src/Functional/BlockPermissionsBlockLibraryControllerTest.php @@ -0,0 +1,100 @@ +<?php + +namespace Drupal\Tests\block_permissions\Functional; + +use Drupal\Core\Url; + +/** + * Tests Block permissions block library controller. + * + * @coversDefaultClass \Drupal\block_permissions\Controller\BlockPermissionsBlockLibraryController + * + * @group block_permissions + */ +class BlockPermissionsBlockLibraryControllerTest extends BlockPermissionsBrowserTestBase { + + /** + * User with permissions to administer core blocks. + * + * @var \Drupal\user\UserInterface + */ + protected $coreBlocksUser; + + /** + * User with permissions to administer system module blocks. + * + * @var \Drupal\user\UserInterface + */ + protected $systemBlocksUser; + + /** + * {@inheritdoc} + */ + protected function setUp(): void { + parent::setUp(); + + $this->coreBlocksUser = $this->drupalCreateUser([ + 'administer blocks', + 'administer blocks provided by core', + "administer block settings for theme $this->defaultTheme", + ]); + $this->systemBlocksUser = $this->drupalCreateUser([ + 'administer blocks', + 'administer blocks provided by system', + "administer block settings for theme $this->secondTheme", + ]); + } + + /** + * Tests blocks visibility on the "/admin/structure/block/library/{theme}". + * + * @covers ::listBlocks + */ + public function testListBlocks() { + $page_title_block_definition = \Drupal::service('plugin.manager.block')->getDefinition('page_title_block'); + $system_branding_block_definition = \Drupal::service('plugin.manager.block')->getDefinition('system_branding_block'); + + // Ensure that the user has the access to the library page of the theme + // which blocks it is able to manage and block is visible. + $this->drupalLogin($this->coreBlocksUser); + $this->drupalGet($this->getBlockAdminLibraryThemeUrl($this->defaultTheme)); + $this->assertBlockLibraryPageHasAccess(); + $this->assertSession()->pageTextContains($page_title_block_definition['admin_label']); + $this->assertSession()->pageTextNotContains($system_branding_block_definition['admin_label']); + $this->drupalGet($this->getBlockAdminLibraryThemeUrl($this->secondTheme)); + $this->assertSession()->statusCodeEquals(403); + + // Ensure the same for the another user. + $this->drupalLogin($this->systemBlocksUser); + $this->drupalGet($this->getBlockAdminLibraryThemeUrl($this->defaultTheme)); + $this->assertSession()->statusCodeEquals(403); + $this->drupalGet($this->getBlockAdminLibraryThemeUrl($this->secondTheme)); + $this->assertBlockLibraryPageHasAccess(); + $this->assertSession()->pageTextNotContains($page_title_block_definition['admin_label']); + $this->assertSession()->pageTextContains($system_branding_block_definition['admin_label']); + } + + /** + * Asserts that the user has the access to the block library page. + */ + protected function assertBlockLibraryPageHasAccess() { + $this->assertSession()->statusCodeEquals(200); + $this->assertSession()->pageTextContains('Place block'); + } + + /** + * Gets the URL of the block library page. + * + * @param string $theme + * Theme name. + * + * @return \Drupal\Core\Url + * A new Url object for a routed URL. + */ + protected function getBlockAdminLibraryThemeUrl($theme) { + return Url::fromRoute('block.admin_library', [ + 'theme' => $theme, + ]); + } + +} diff --git a/web/modules/block_permissions/tests/src/Functional/BlockPermissionsBrowserTestBase.php b/web/modules/block_permissions/tests/src/Functional/BlockPermissionsBrowserTestBase.php new file mode 100644 index 0000000000..0fa3bb0c3a --- /dev/null +++ b/web/modules/block_permissions/tests/src/Functional/BlockPermissionsBrowserTestBase.php @@ -0,0 +1,60 @@ +<?php + +namespace Drupal\Tests\block_permissions\Functional; + +use Drupal\Tests\BrowserTestBase; + +/** + * Base class for block_permissions tests. + */ +abstract class BlockPermissionsBrowserTestBase extends BrowserTestBase { + + /** + * {@inheritdoc} + */ + protected static $modules = [ + 'block', + 'block_permissions', + ]; + + /** + * {@inheritdoc} + */ + protected $defaultTheme = 'stark'; + + /** + * Second theme. + * + * @var string + */ + protected $secondTheme = 'bartik'; + + /** + * Page title block. + * + * @var \Drupal\block\BlockInterface + */ + protected $pageTitleBlock; + + /** + * System branding block. + * + * @var \Drupal\block\BlockInterface + */ + protected $systemBrandingBlock; + + /** + * {@inheritdoc} + */ + protected function setUp(): void { + parent::setUp(); + + // Enable second theme. + \Drupal::service('theme_installer')->install([$this->secondTheme], TRUE); + + // Add blocks to default theme. + $this->pageTitleBlock = $this->drupalPlaceBlock('page_title_block'); + $this->systemBrandingBlock = $this->drupalPlaceBlock('system_branding_block'); + } + +} -- GitLab