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